// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                            ViewerWindowManager.C                          //
// ************************************************************************* //
#include <ViewerWindowManager.h>

#include <visitstream.h>
#include <visit-config.h>

// for off-screen save window
#include <InitVTK.h>

#include <AnimationAttributes.h>
#include <AnnotationObjectList.h>
#include <DataNode.h>
#include <DatabaseCorrelation.h>
#include <DatabaseCorrelationList.h>
#include <EngineKey.h>
#include <FileFunctions.h>
#include <GlobalAttributes.h>
#include <ImproperUseException.h>
#include <InteractorAttributes.h>
#include <KeyframeAttributes.h>
#include <LightAttributes.h>
#include <LightList.h>
#include <Line.h>
#include <OperatorPluginInfo.h>
#include <OperatorPluginManager.h>
#include <PlotPluginInfo.h>
#include <PlotPluginManager.h>
#include <PrinterAttributes.h>
#include <RenderingAttributes.h>
#include <SaveWindowAttributes.h>
#include <SelectionList.h>
#include <SelectionProperties.h>
#include <SelectionSummary.h>
#include <StackTimer.h>
#include <StringHelpers.h>
#include <TimingsManager.h>
#include <View2DAttributes.h>
#include <View3DAttributes.h>
#include <ViewAxisArrayAttributes.h>
#include <ViewCurveAttributes.h>
#include <ViewerFactory.h>
#include <VisItException.h>
#include <VisWindow.h>
#include <VisitInteractor.h>
#include <WindowInformation.h>

#include <ViewerActionManager.h>
#include <ViewerDatabaseCorrelationMethods.h>
#include <ViewerEngineManagerInterface.h>
#include <ViewerFileServerInterface.h>
#include <ViewerInternalCommands.h>
#include <ViewerMessaging.h>
#include <ViewerOperator.h>
#include <ViewerOperatorFactory.h>
#include <ViewerPlotList.h>
#include <ViewerPlot.h>
#include <ViewerProperties.h>
#include <ViewerQueryManager.h>
#include <ViewerState.h>
#include <ViewerStateManager.h>
#include <ViewerText.h>
#include <ViewerVariableMethods.h>
#include <ViewerWindow.h>
#include <ViewerWindowManagerAttributes.h>

#include <avtCallback.h>
#include <avtDatabaseMetaData.h>
#include <avtImage.h>
#include <avtImageTiler.h>
#include <avtMultiWindowSaver.h>
#include <avtFileWriter.h>
#include <avtToolInterface.h>

#include <vtkDataArray.h>
#include <vtkImageData.h>
#include <vtkPointData.h>
#include <vtkUnsignedCharArray.h>

#include <DebugStream.h>

#include <algorithm>
#include <stdio.h> // for sscanf
#include <string.h>
#if !defined(_WIN32)
#include <signal.h> // for signal
#endif

using std::string;

//
// Storage for static data elements.
//
ViewerWindowManager *ViewerWindowManager::instance=0;
const int ViewerWindowManager::maxWindows=16;
const int ViewerWindowManager::maxLayouts=6;
const int ViewerWindowManager::validLayouts[]={1, 4, 9, 2, 6, 8};

ViewerWindowManagerAttributes *ViewerWindowManager::windowAtts=0;

AnnotationAttributes *ViewerWindowManager::annotationDefaultAtts=0;
InteractorAttributes *ViewerWindowManager::interactorDefaultAtts=0;
LightList *ViewerWindowManager::lightListDefaultAtts=0;
AnnotationObjectList *ViewerWindowManager::defaultAnnotationObjectList = 0;

// ****************************************************************************
//  Method: ViewerWindowManager constructor
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
//  Modifications:
//    Brad Whitlock, Tue Nov 7 09:42:35 PDT 2000
//    Changed to use ViewerWindow.
//
//    Brad Whitlock, Mon Nov 27 14:12:26 PST 2000
//    Made it inherit from QObject.
//
//    Eric Brugger, Tue Aug 21 10:17:24 PDT 2001
//    I added the registration of a callback for the view changing.
//
//    Kathleen Bonnell, Fri Sep 28 17:12:11 PDT 2001
//    Initialize shiftX, shiftY, preshiftX, preshiftY to eliminate UMR.
//
//    Brad Whitlock, Tue Oct 9 17:23:16 PST 2001
//    Added code to set the tool callback.
//
//    Eric Brugger, Fri Oct 26 14:33:26 PDT 2001
//    I added a timer for playing animations.
//
//    Eric Brugger, Fri Nov  2 12:39:55 PST 2001
//    I added a data member to keep track of whether the window has been
//    referenced before.
//
//    Kathleen Bonnell, Fri May 10 16:27:40 PDT 2002
//    Added data member to keep track of the designated lineout window.
//
//    Hank Childs, Fri May 24 11:20:34 PDT 2002
//    Replaced image writer with more general file writer.
//
//    Brad Whitlock, Wed Jul 24 14:44:52 PST 2002
//    I initialized the lastAnimation member.
//
//    Brad Whitlock, Wed Jan 22 16:44:26 PST 2003
//    I initialized the windowsIconified member.
//
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I added undoing of curve views.
//
//    Kathleen Bonnell, Thu Apr  1 19:13:59 PST 2004
//    Added timeQueryWindow.
//
//    Eric Brugger, Thu Jun 30 11:43:22 PDT 2005
//    Added a 2 x 3 layout and removed the 4 x 4 layout.
//
//    Brad Whitlock, Tue Mar 7 17:32:54 PST 2006
//    I removed most of the view stacking members.
//
//    Brad Whitlock, Mon Feb 12 17:38:27 PST 2007
//    Changed base class.
//
// ****************************************************************************

ViewerWindowManager::ViewerWindowManager() : ViewerBase()
{
    layout       = 1;
    layoutIndex  = 0;
    activeWindow = 0;

    screenX      = 0;
    screenY      = 0;
    screenWidth  = 800;
    screenHeight = 800;
    borderTop    = 32;
    borderBottom = 8;
    borderLeft   = 8;
    borderRight  = 8;

    shiftX = 0;
    shiftY = 0;
    preshiftX = 0;
    preshiftY = 0;

    windowsHidden = true;
    windowsIconified = false;

    nWindows      = 0;
    windows       = new ViewerWindow*[maxWindows];
    x_locations   = new int[maxWindows];
    y_locations   = new int[maxWindows];
    memset(windows, 0, maxWindows * sizeof(ViewerWindow *));
    referenced    = new bool[maxWindows];
    referenced[0] = true;
    for (int i = 1; i < maxWindows; i++)
    {
        referenced[i] = false;
    }

    windowLimits = new WindowLimits*[6];
    windowLimits[0] = new WindowLimits[1];
    windowLimits[1] = new WindowLimits[4];
    windowLimits[2] = new WindowLimits[9];
    windowLimits[3] = new WindowLimits[2];
    windowLimits[4] = new WindowLimits[6];
    windowLimits[5] = new WindowLimits[8];

    fileWriter  = new avtFileWriter();

    InitWindowLimits();

    //
    // Register a callback function to be called when the view changes from
    // using interactors.
    //
    VisitInteractor::RegisterViewCallback(ViewCallback);

    //
    // Register a callback function to be called when tools want to inform
    // the viewer of a new tool state.
    //
    avtToolInterface::SetCallback(ToolCallback);

    viewStacking = true;
    lineoutWindow = -1;
    timeQueryWindow = -1;

    AnimationCB = NULL;
    AnimationCBData = NULL;
    UpdateWindowInformationCB = NULL;
    UpdateWindowInformationCBData = NULL;
}

// ****************************************************************************
//  Method: ViewerWindowManager destructor
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
//  Modifications:
//
// ****************************************************************************

ViewerWindowManager::~ViewerWindowManager()
{
    //
    // This should never be executed.
    //
}

// ****************************************************************************
//  Method: ViewerWindowManager::Instance
//
//  Purpose:
//    Return a pointer to the sole instance of the ViewerWindowManager
//    class.
//
//  Returns:    A pointer to the sole instance of the ViewerWindowManager
//              class.
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
// ****************************************************************************

ViewerWindowManager *
ViewerWindowManager::Instance()
{
    //
    // If the sole instance hasn't been instantiated, then instantiate it.
    //
    if (instance == 0)
    {
        instance = new ViewerWindowManager;
    }

    return instance;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetBorders
//
//  Purpose:
//    Set the sizes of the window borders used by the window manager.
//
//  Arguments:
//    windowBorders  The window borders.  The format for the string is
//                   "top,bottom,left,right".
//
//  Programmer: Eric Brugger
//  Creation:   September 13, 2000
//
// ****************************************************************************

void
ViewerWindowManager::SetBorders(const char *windowBorders)
{
    //
    // Check the arguments.
    //
    if (windowBorders == 0)
    {
        return;
    }

    //
    // Parse the borders string into its pieces.  This will only parse
    // the borders string if it is in the form Top,Bottom,Left,Right.
    //
    sscanf(windowBorders, "%d,%d,%d,%d", &borderTop, &borderBottom,
           &borderLeft, &borderRight);

    //
    // Reset the window limits structure using the new border information.
    //
    InitWindowLimits();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetShift
//
//  Purpose:
//    Set the amounts of the window shift needed by the window manager.
//
//  Arguments:
//    windowShift    The window shift.  The format for the string is "X,Y"
//
//  Programmer: Jeremy Meredith
//  Creation:   July 19, 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetShift(const char *windowShift)
{
    //
    // Check the arguments.
    //
    if (windowShift == 0)
    {
        return;
    }

    //
    // Parse the shift string into its pieces.  This will only parse
    // the shift string if it is in the form X,Y.
    //
    sscanf(windowShift, "%d,%d", &shiftX, &shiftY);

    //
    // Reset the window limits structure using the new border information.
    //
    InitWindowLimits();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetPreshift
//
//  Purpose:
//    Set the amounts of the window preshift needed by the window manager.
//
//  Arguments:
//    windowPreshift  The window preshift. The format for the string is "X,Y"
//
//  Programmer: Jeremy Meredith
//  Creation:   September 14, 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetPreshift(const char *windowPreshift)
{
    //
    // Check the arguments.
    //
    if (windowPreshift == 0)
    {
        return;
    }

    //
    // Parse the preshift string into its pieces.  This will only parse
    // the preshift string if it is in the form X,Y.
    //
    sscanf(windowPreshift, "%d,%d", &preshiftX, &preshiftY);
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetGeometry
//
//  Purpose:
//    Set the area to be used by the window manager.  The window manager
//    will keep the windows in the area as best as possible.
//
//  Arguments:
//    windowGeometry  The area to keep the windows.  It is specified as
//                    an X Window System geometry string.
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
// ****************************************************************************

void
ViewerWindowManager::SetGeometry(const char *windowGeometry)
{
    //
    // Check the arguments.
    //
    if (windowGeometry == 0)
    {
        return;
    }

    //
    // Parse the geometry string into its pieces.  This will only parse
    // the geometry if it is in the form WidthxHeight+X+Y.  In X the width
    // and height or x and y position are optional, also a minus sign can
    // be used instead of a plus sign.
    //
    sscanf(windowGeometry, "%dx%d+%d+%d", &screenWidth, &screenHeight,
           &screenX, &screenY);

    //
    // Reset the window limits structure using the new virtual screen
    // information.
    //
    InitWindowLimits();
}

// ****************************************************************************
//  Method: ViewerWindowManager::AddWindow
//
//  Purpose:
//    Add a new window.  The new window becomes the active window.
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
//  Modifications:
//    Brad Whitlock, Mon Apr 23 14:38:41 PST 2001
//    Sent an error message to the GUI instead of cerr.
//
//    Brad Whitlock, Fri Jul 27 09:48:55 PDT 2001
//    Added code to send the new view to the client.
//
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
//    Brad Whitlock, Thu Aug 30 09:42:40 PDT 2001
//    Added code to update the client annotation attributes.
//
//    Brad Whitlock, Fri Sep 14 15:26:17 PST 2001
//    Added code to update the client's light list.
//
//    Eric Brugger, Wed Nov 21 12:12:45 PST 2001
//    I added animation attributes.
//
//    Kathleen Bonnell, Tue Nov 27 16:03:00 PST 2001
//    Added pick attributes.
//
//    Brad Whitlock, Mon Feb 4 10:27:29 PDT 2002
//    Moved the update code into UpdateAllAtts.
//
//    Brad Whitlock, Tue Oct 15 16:45:35 PST 2002
//    Added a boolean flag that optionally tells the method to copy attributes.
//
//    Mark C. Miller, Mon Jan 13 16:52:33 PST 2003
//    Added code to register the external render callback
//
//    Eric Brugger, Thu Mar 13 10:50:11 PST 2003
//    I implemented CloneWindowOnFirstRef mode, which clones the current
//    window to the newly activated window when it is first referenced.
//
//    Eric Brugger, Fri Apr 11 14:03:19 PDT 2003
//    I moved part of the functionality to SimpleAddWindow and added a
//    call to it.
//
//    Brad Whitlock, Fri Nov 7 10:03:35 PDT 2003
//    I added code to copy the annotation object list.
//
//    Brad Whitlock, Tue Jan 27 17:26:46 PST 2004
//    I changed the copy code a little bit.
//
//    Brad Whitlock, Thu Feb 17 14:23:24 PST 2005
//    Added bool to ViewerPlotList::CopyFrom.
//
//    Brad Whitlock, Fri Apr 15 17:06:49 PST 2005
//    Added code to copy action data.
//
//    Brad Whitlock, Mon Mar 26 14:53:21 PST 2007
//    Allow legend annotation objects from being copied to the new window.
//
//    Brad Whitlock, Wed Apr 30 09:44:17 PDT 2008
//    Added tr().
//
//    Brad Whitlock, Fri Aug 13 16:36:28 PDT 2010
//    Fix legend renaming.
//
//    Brad Whitlock, Thu Jan 13 22:56:32 PST 2011
//    I applied Cyrus' suggested fix for copying the sources. Looks good.
//
//    Gunther H. Weber, Fri Jul 15 16:54:05 PDT 2011
//    Make cloned window same size as original window.
//
// ****************************************************************************

void
ViewerWindowManager::AddWindow(bool copyAtts)
{
    //
    // Add a simple window.
    //
    int       windowIndex;

    windowIndex = SimpleAddWindow();
    if (windowIndex == -1)
    {
        GetViewerMessaging()->Error(TR("The maximum number of windows was exceeded."));
        return;
    }

    //
    // Copy attributes.
    //
    if (windowIndex != activeWindow)
    {
        ViewerWindow *dest = windows[windowIndex];
        ViewerWindow *src = windows[activeWindow];
        if (copyAtts || GetViewerState()->GetGlobalAttributes()->GetCloneWindowOnFirstRef())
        {
            int w, h;
            src->GetWindowSize(w, h);
            dest->SetWindowSize(w,h);

            dest->CopyGeneralAttributes(src);
            dest->CopyAnnotationAttributes(src);
            dest->CopyLightList(src);
            dest->CopyViewAttributes(src);

            StringStringMap nameMap = dest->GetPlotList()->CopyFrom(src->
                GetPlotList(), true);
            dest->GetActionManager()->CopyFrom(src->GetActionManager());

            dest->CopyAnnotationObjectList(src, nameMap, true);
        }
        else
        {
            // We always want to copy the active sources -- but not
            // the plots themselves.
            dest->GetPlotList()->CopyFrom(src->GetPlotList(), false);
        }
    }
    referenced[windowIndex] = true;

    // Always copy the Interactor atts
    if (windowIndex != activeWindow)
    {
        windows[windowIndex]->CopyInteractorAtts(windows[activeWindow]);
    }

    //
    // Now that the view has been set up (and other things), we can set the
    // window atts.  This is to make sure that we turn on view locking after
    // the view has been set.
    //
    SetWindowAttributes(windowIndex, copyAtts);

    //
    // Make the new window the active window.
    //
    activeWindow = windowIndex;

    //
    // Update all the client window attributes.
    //
    UpdateAllAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::CloneWindow
//
// Purpose:
//   Creates a new window and copies the current window's attributes to it
//   before making it active.
//
// Programmer: Brad Whitlock
// Creation:   Tue Oct 15 16:43:45 PST 2002
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::CloneWindow()
{
    AddWindow(true);
}

// ****************************************************************************
//  Method: ViewerWindowManager::ClearAllWindows
//
//  Purpose:
//      Clear all the windows.
//
//  Programmer: Eric Brugger
//  Creation:   October 4, 2000
//
//  Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::ClearAllWindows()
{
    //
    // Loop over all the windows, if the window exists, clear all the
    // plots in the associated animation.
    //
    for (int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        ClearWindow(windowIndex);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ClearWindow
//
// Purpose:
//   Clears the window with the specified index.
//
// Arguments:
//   windowIndex : A zero-origin integer that identifies the window
//                 to clear. If windowIndex happens to be -1, use the
//                 active window's index.
//
// Programmer: Brad Whitlock
// Creation:   Tue Nov 7 09:50:31 PDT 2000
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::ClearWindow(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
        windows[index]->ClearWindow();
}

// ****************************************************************************
// Method: ViewerWindowManager::CopyAnnotationsToWindow
//
// Purpose:
//   Copies the annotation attributes from one window to another window.
//
// Arguments:
//   from : The source window.
//   to   : The destination window.
//
// Programmer: Brad Whitlock
// Creation:   Thu Jun 27 16:47:26 PST 2002
//
// Modifications:
//   Brad Whitlock, Thu Nov 6 17:15:32 PST 2003
//   I made it copy the annotation object list.
//
//   Brad Whitlock, Mon Mar 26 14:54:03 PST 2007
//   Prevent legend annotation objects from being copied.
//
// ****************************************************************************

void
ViewerWindowManager::CopyAnnotationsToWindow(int from, int to)
{
    if(from < 0 || from >= maxWindows)
        return;
    if(to < 0 || to >= maxWindows)
        return;

    // If the Window pointers are valid then perform the operation.
    if(windows[from] != 0 && windows[to] != 0)
    {
        windows[to]->CopyAnnotationAttributes(windows[from]);
        windows[to]->CopyAnnotationObjectList(windows[from], StringStringMap(), false);
        if(to == activeWindow)
        {
            UpdateAnnotationAtts();
            UpdateAnnotationObjectList();
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::CopyLightingToWindow
//
// Purpose:
//   Copies the lighting attributes from one window to another window.
//
// Arguments:
//   from : The source window.
//   to   : The destination window.
//
// Programmer: Brad Whitlock
// Creation:   Thu Jun 27 16:47:26 PST 2002
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::CopyLightingToWindow(int from, int to)
{
    if(from < 0 || from >= maxWindows)
        return;
    if(to < 0 || to >= maxWindows)
        return;

    // If the Window pointers are valid then perform the operation.
    if(windows[from] != 0 && windows[to] != 0)
    {
        windows[to]->CopyLightList(windows[from]);
        if(to == activeWindow)
            UpdateLightListAtts();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::CopyViewToWindow
//
// Purpose:
//   Copies the view attributes from one window to another window.
//
// Arguments:
//   from : The source window.
//   to   : The destination window.
//
// Programmer: Brad Whitlock
// Creation:   Thu Jun 27 16:47:26 PST 2002
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::CopyViewToWindow(int from, int to)
{
    if(from < 0 || from >= maxWindows)
        return;
    if(to < 0 || to >= maxWindows)
        return;

    // If the Window pointers are valid then perform the operation.
    if(windows[from] != 0 && windows[to] != 0)
    {
        windows[to]->CopyViewAttributes(windows[from]);
        if(to == activeWindow)
            UpdateViewAtts();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::CopyPlotListToWindow
//
// Purpose:
//   Copies the plots from one window to another window.
//
// Arguments:
//   from : The source window.
//   to   : The destination window.
//
// Programmer: Brad Whitlock
// Creation:   Tue Oct 15 16:39:42 PST 2002
//
// Modifications:
//   Brad Whitlock, Thu Feb 17 14:23:24 PST 2005
//   Added bool to ViewerPlotList::CopyFrom.
//
// ****************************************************************************

void
ViewerWindowManager::CopyPlotListToWindow(int from, int to)
{
    if(from < 0 || from >= maxWindows)
        return;
    if(to < 0 || to >= maxWindows)
        return;

    // If the Window pointers are valid then perform the operation.
    if(windows[from] != 0 && windows[to] != 0)
    {
        StringStringMap nameMap = windows[to]->GetPlotList()->CopyFrom(
            windows[from]->GetPlotList(),true);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::DeleteWindow
//
//  Purpose:
//    Delete the currently active window.
//
//  Programmer: Eric Brugger
//  Creation:   September 13, 2000
//
//  Modifications:
//    Brad Whitlock, Mon Nov 27 14:04:51 PST 2000
//    Added code to emit a deleteWindow signal.
//
//    Brad Whitlock, Fri Jul 27 09:48:30 PDT 2001
//    Added code to send the new view to the client.
//
//    Brad Whitlock, Wed Aug 22 11:29:11 PDT 2001
//    Moved the code into the new DeleteWindow method.
//
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
//    Eric Brugger, Wed Nov 21 12:12:45 PST 2001
//    I added animation attributes.
//
// ****************************************************************************

void
ViewerWindowManager::DeleteWindow()
{
    //
    // Delete the active window.
    //
    DeleteWindow(windows[activeWindow]);
}

// ****************************************************************************
//  Method: ViewerWindowManager::DeleteWindow
//
//  Purpose:
//    Deletes the specified viewer window.
//
//  Arguments:
//    win       A pointer to the viewer window that we want to delete.
//
//  Programmer: Brad Whitlock
//  Creation:   Wed Aug 22 11:38:24 PDT 2001
//
//  Modifications:
//    Brad Whitlock, Thu Aug 30 09:43:51 PDT 2001
//    Added code to update the client's annotation attributes.
//
//    Brad Whitlock, Fri Sep 14 15:25:44 PST 2001
//    Added code to update the client's light list.
//
//    Kathleen Bonnell, Tue Nov 27 16:03:00 PST 2001
//    Added pick attributes.
//
//    Brad Whitlock, Mon Feb 4 10:26:48 PDT 2002
//    Moved the update code into UpdateAllAtts.
//
//    Kathleen Bonnell, Fri May 10 16:27:40 PDT 2002
//    Added call to ResetLineoutDesignation.
//
//    Hank Childs, Wed Jul 10 21:46:55 PDT 2002
//    Unlock a window before deleting it.
//
//    Kathleen Bonnell, Wed Jul 31 16:43:43 PDT 2002
//    Notify ViewerQueryManager that a window is being deleted.
//
//    Kathleen Bonnell, Wed Apr 14 16:19:18 PDT 2004
//    Added call to ResetTimeQueryDesignation.
//
//    Brad Whitlock, Wed Apr 30 09:44:37 PDT 2008
//    Added tr().
//
//    Hank Childs, Thu Jul 28 04:59:54 PDT 2011
//    Make sure the new active window is DeIconified before deleting the
//    current window.  Otherwise, Qt might decide there are no active
//    windows and quit.
//
// ****************************************************************************

void
ViewerWindowManager::DeleteWindow(ViewerWindow *win)
{
    //
    // The user can't delete the last active window.
    //
    if(nWindows <= 1)
    {
        GetViewerMessaging()->Error(TR("Can't delete the last window."));
        return;
    }

    //
    // Find the window index of the window we're deleting.
    //
    int windowIndex;
    for(windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if(windows[windowIndex] == win && windows[windowIndex] != 0)
        {
            break;
        }
    }

    //
    // If we could not find a window index for the specified window, return.
    //
    if(windowIndex >= maxWindows)
        return;

    //
    // Find the first window that isn't the window we're deleting.
    //
    int newWindowIndex;
    for(newWindowIndex = 0; newWindowIndex < maxWindows; ++newWindowIndex)
    {
        if(windows[newWindowIndex] != win && windows[newWindowIndex] != 0)
        {
            break;
        }
    }

    //
    // Make sure this window is de-iconified.  If not, Qt might decide
    // there are no active windows and quit.
    //
    windows[newWindowIndex]->DeIconify();

    //
    // Tell some of our other mechanisms that this window is going away.
    //
    ViewerQueryManager::Instance()->Delete(win);
    ResetLineoutDesignation(windowIndex);
    ResetTimeQueryDesignation(windowIndex);
    if (windows[windowIndex]->GetViewIsLocked())
    {
        ToggleLockViewMode(windowIndex);
    }

    //
    // Delete the active animation and window.
    //
    delete windows[windowIndex];
    windows[windowIndex] = 0;
    referenced[windowIndex] = false;
    nWindows--;

    //
    // Make the lowest number window the new active window if we're deleting
    // the active window.
    //
    if(windowIndex == activeWindow)
        activeWindow = newWindowIndex;

    //
    // Send a message to the client that indicates which window was deleted.
    //
    ViewerText msg(TR("Window %1 was deleted.").arg(windowIndex + 1));
    GetViewerMessaging()->Message(msg);

    //
    // Update all of the client window attributes
    //
    UpdateAllAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::DisableRedraw
//
// Purpose:
//   Disallows redraws in the specified window.
//
// Programmer: Brad Whitlock
// Creation:   Wed Sep 19 15:14:41 PST 2001
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::DisableRedraw(int windowIndex)
{
    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->DisableUpdates();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::FileInUse
//
// Purpose:
//   Determines if a file is in use in plots across all windows.
//
// Arguments:
//   host   : The hostname.
//   dbName : The datanase name.
//
// Programmer: Brad Whitlock
// Creation:   Fri Feb 8 10:32:14 PDT 2002
//
// Modifications:
//   Brad Whitlock, Fri Mar 26 14:42:50 PST 2004
//   I made it use strings.
//
// ****************************************************************************

bool
ViewerWindowManager::FileInUse(const std::string &host,
    const std::string &dbName) const
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
        {
            if(windows[i]->GetPlotList()->FileInUse(host, dbName))
                return true;
        }
    }

    return false;
}

// ****************************************************************************
// Method: ViewerWindowManager::IconifyAllWindows
//
// Purpose:
//   Iconifies all viewer windows.
//
// Programmer: Brad Whitlock
// Creation:   Thu Apr 19 11:14:10 PDT 2001
//
// Modifications:
//   Brad Whitlock, Wed Jan 22 16:45:24 PST 2003
//   I added code to turn off animation.
//
//   Brad Whitlock, Wed Feb 7 17:01:41 PST 2007
//   Added call to AlternateDisplayIconify.
//
// ****************************************************************************

void
ViewerWindowManager::IconifyAllWindows()
{
    // Disable animation.
    windowsIconified = true;
    UpdateAnimationTimer();

    // Iconify the windows
    for(int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if(windows[windowIndex] != 0)
        {
            windows[windowIndex]->Iconify();

            // Show all of the windows for plots that have alternate displays.
            for(int i = 0; i < windows[windowIndex]->GetPlotList()->GetNumPlots(); ++i)
                windows[windowIndex]->GetPlotList()->GetPlot(i)->AlternateDisplayIconify();
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::DeIconifyAllWindows
//
// Purpose:
//   De-iconifies all viewer windows.
//
// Programmer: Brad Whitlock
// Creation:   Thu Apr 19 11:14:29 PDT 2001
//
// Modifications:
//   Brad Whitlock, Wed Jan 22 16:46:15 PST 2003
//   I added code to enable animation if it was going before the windows
//   were iconified.
//
//   Brad Whitlock, Wed Feb 7 17:01:18 PST 2007
//   Added call to AlternateDisplayDeIconify.
//
// ****************************************************************************

void
ViewerWindowManager::DeIconifyAllWindows()
{
    // Disable animation.
    windowsIconified = false;
    UpdateAnimationTimer();

    for(int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if(windows[windowIndex] != 0)
        {
            windows[windowIndex]->DeIconify();

            // Show all of the windows for plots that have alternate displays.
            for(int i = 0; i < windows[windowIndex]->GetPlotList()->GetNumPlots(); ++i)
                windows[windowIndex]->GetPlotList()->GetPlot(i)->AlternateDisplayDeIconify();
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ShowAllWindows
//
// Purpose:
//   Shows all viewer windows.
//
// Programmer: Sean Ahern
// Creation:   Tue Apr 16 12:37:12 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 7 16:59:40 PST 2007
//   Added call to AlternateDisplayShow.
//
// ****************************************************************************

void
ViewerWindowManager::ShowAllWindows()
{
    windowsHidden = false;

    for (int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if (windows[windowIndex] != 0)
        {
            if (windows[windowIndex]->GetRealized() == true)
            {
                windows[windowIndex]->Show();
            }
            else
            {
                windows[windowIndex]->SetLocation(
                    x_locations[windowIndex] - preshiftX,
                    y_locations[windowIndex] - preshiftY);
                windows[windowIndex]->Realize();
                windows[windowIndex]->SetLocation(x_locations[windowIndex],
                                                  y_locations[windowIndex]);
            }

            // Show all of the windows for plots that have alternate displays.
            for(int i = 0; i < windows[windowIndex]->GetPlotList()->GetNumPlots(); ++i)
                windows[windowIndex]->GetPlotList()->GetPlot(i)->AlternateDisplayShow();
        }
    }


}

// ****************************************************************************
// Method: ViewerWindowManager::HideAllWindows
//
// Purpose:
//   Hides all viewer windows.
//
// Programmer: Sean Ahern
// Creation:   Tue Apr 16 12:37:12 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 7 17:00:16 PST 2007
//   Added call to AlternateDisplayHide.
//
// ****************************************************************************

void
ViewerWindowManager::HideAllWindows()
{
    windowsHidden = true;

    for (int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if (windows[windowIndex] != 0)
        {
            windows[windowIndex]->Hide();

            // Show all of the windows for plots that have alternate displays.
            for(int i = 0; i < windows[windowIndex]->GetPlotList()->GetNumPlots(); ++i)
                windows[windowIndex]->GetPlotList()->GetPlot(i)->AlternateDisplayHide();
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::InvertBackgroundColor
//
// Purpose:
//   This is a Qt slot function that is called when the popup menu's invert
//   button is clicked.
//
// Arguments:
//   windowIndex : The index of the window that called this method.
//
// Programmer: Brad Whitlock
// Creation:   August 26, 2001
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::InvertBackgroundColor(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->InvertBackgroundColor();
    }

    // Update the annotation attributes and send them to the client if the
    // windowIndex == activeWindow.
    if(index == activeWindow)
        UpdateAnnotationAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::RedrawWindow
//
// Purpose:
//   Redraws the specified window.
//
// Programmer: Brad Whitlock
// Creation:   Wed Sep 19 15:14:41 PST 2001
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::RedrawWindow(int windowIndex)
{
    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->RedrawWindow();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::MoveWindow
//
// Purpose:
//   Moves the specified window.
//
// Programmer: Brad Whitlock
// Creation:   Thu Nov 17 17:18:45 PST 2005
//
// Modifications:
//   Brad Whitlock, Wed Apr 30 09:45:50 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::MoveWindow(int windowIndex, int x, int y)
{
    if(windowIndex < 0 || windowIndex >= maxWindows)
    {
        ViewerText msg(TR("Invalid window index (windowIndex = %1)").arg(windowIndex));
        GetViewerMessaging()->Error(msg);
        return;
    }

    if(windows[windowIndex] != 0)
    {
        windows[windowIndex]->SetLocation(x + borderLeft - shiftX,
                                          y + borderTop  - shiftY);
    }
    else
        GetViewerMessaging()->Error(TR("The specified window does not exist."));
}

// ****************************************************************************
// Method: ViewerWindowManager::MoveAndResizeWindow
//
// Purpose:
//   Moves and resizes the specified window.
//
// Programmer: Brad Whitlock
// Creation:   Thu Nov 17 17:18:59 PST 2005
//
// Modifications:
//   Brad Whitlock, Wed Apr 30 09:45:50 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::MoveAndResizeWindow(int windowIndex, int x, int y,
    int w, int h)
{
    if(windowIndex < 0 || windowIndex >= maxWindows)
    {
        ViewerText msg(TR("Invalid window index (windowIndex = %1)").
                          arg(windowIndex));
        GetViewerMessaging()->Error(msg);
        return;
    }

    if(windows[windowIndex] != 0)
    {
        windows[windowIndex]->SetLocation(x + borderLeft - shiftX,
                                          y + borderTop  - shiftY);
        int borderWidth  = borderLeft + borderRight;
        int borderHeight = borderTop  + borderBottom;
        windows[windowIndex]->SetSize(w - borderWidth, h - borderHeight);

        UpdateWindowInformation(WINDOWINFO_WINDOWSIZE, windowIndex);
    }
    else
        GetViewerMessaging()->Error(TR("The specified window does not exist."));
}

// ****************************************************************************
// Method: ViewerWindowManager::ResizeWindow
//
// Purpose:
//   Resizes the specified window.
//
// Programmer: Brad Whitlock
// Creation:   Thu Nov 17 17:19:19 PST 2005
//
// Modifications:
//   Brad Whitlock, Wed Apr 30 09:45:50 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::ResizeWindow(int windowIndex, int w, int h)
{
    if(windowIndex < 0 || windowIndex >= maxWindows)
    {
        ViewerText msg(TR("Invalid window index (windowIndex = %1)").
                          arg(windowIndex));
        GetViewerMessaging()->Error(msg);
        return;
    }

    if(windows[windowIndex] != 0)
    {
        int borderWidth  = borderLeft + borderRight;
        int borderHeight = borderTop  + borderBottom;
        windows[windowIndex]->SetSize(w - borderWidth, h - borderHeight);

        UpdateWindowInformation(WINDOWINFO_WINDOWSIZE, windowIndex);
    }
    else
        GetViewerMessaging()->Error(TR("The specified window does not exist."));
}

// ****************************************************************************
//  Method: ViewerWindowManager::ResetView
//
//  Purpose:
//    This method resets the view for the specified window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Nov 7 13:31:14 PST 2000
//
//  Modifications:
//    Brad Whitlock, Fri Jul 27 09:47:20 PDT 2001
//    Added code to send the new view to the client.
//
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
// ****************************************************************************

void
ViewerWindowManager::ResetView(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->ResetView();

        //
        // Send the new view info to the client.
        //
        UpdateViewAtts(index);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::RecenterView
//
//  Purpose:
//    This method recenters the view for the specified window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Eric Brugger
//  Creation:   February 23, 2001
//
//  Modifications:
//    Brad Whitlock, Fri Jul 27 09:46:52 PDT 2001
//    Added code to sent the new view to the client.
//
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
// ****************************************************************************

void
ViewerWindowManager::RecenterView(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->RecenterView();

        //
        // Send the new view info to the client.
        //
        UpdateViewAtts(index);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SetCenterOfRotation
//
// Purpose:
//   Sets the center of rotation using a world space coordinate.
//
// Arguments:
//   windowIndex : The index of the window whose center we're setting.
//   x,y,z       : The new world space center of rotation.
//
// Programmer: Brad Whitlock
// Creation:   Wed Jan 7 09:59:50 PDT 2004
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetCenterOfRotation(int windowIndex,
    double x, double y, double z)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    if(windows[windowIndex] != 0)
    {
        windows[windowIndex]->SetCenterOfRotation(x, y, z);

        //
        // Send the new view info to the client.
        //
        UpdateViewAtts(windowIndex);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ChooseCenterOfRotation
//
// Purpose:
//   Chooses the center of rotation using the surface that lies at point
//   sx,sy in the vis window's screen.
//
// Arguments:
//   sx, sy : The screen point. sx and sy in [0,1].
//
// Programmer: Brad Whitlock
// Creation:   Wed Jan 7 09:58:54 PDT 2004
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::ChooseCenterOfRotation(int windowIndex,
    double sx, double sy)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    if(windows[windowIndex] != 0)
    {
        windows[windowIndex]->ChooseCenterOfRotation(sx, sy);

        //
        // Send the new view info to the client.
        //
        UpdateViewAtts(windowIndex);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SaveWindow
//
//  Purpose:
//    Saves the screen captured contents of the window with the specified index
//
//  Arguments:
//    windowIndex A zero-origin integer that identifies the window
//                to save. If windowIndex happens to be -1, use the
//                active window's index.
//
//  Programmer: Hank Childs
//  Creation:   February 11, 2001
//
//  Modifications:
//    Brad Whitlock, Mon Feb 12 14:29:11 PST 2001
//    Modified code to support saving tiled images and shipping images to
//    the engine.
//
//    Brad Whitlock, Tue May 1 11:32:38 PDT 2001
//    Added code to send status to the GUI.
//
//    Brad Whitlock, Tue Aug 21 10:08:34 PDT 2001
//    Fixed an off by one error in the message that is sent to the client.
//
//    Brad Whitlock, Fri Sep 21 13:17:22 PST 2001
//    Modified the code so window saving status and messages are kept around
//    longer.
//
//    Brad Whitlock, Wed Jan 23 14:16:09 PST 2002
//    Enabled JPEG images.
//
//    Hank Childs, Thu May 23 18:47:33 PDT 2002
//    Added support for writing datasets to files, as well as images.
//
//    Hank Childs, Mon May 27 11:56:02 PDT 2002
//    Added binary argument to writing a file.  Also add exception handling.
//
//    Jeremy Meredith, Thu Jul 25 11:52:24 PDT 2002
//    Added code to stuff the true filename back into the attributes.
//    This way the CLI/GUI may use the real filename used to save.
//
//    Jeremy Meredith, Fri Jul 26 14:06:52 PDT 2002
//    Added a call to compact parallel domains before saving a dataset.
//
//    Brad Whitlock, Tue Nov 12 13:57:10 PST 2002
//    Added code to catch ImproperUseException when setting the file format.
//
//    Hank Childs, Mon Feb 24 18:14:52 PST 2003
//    Be more leery of NULL return types.
//
//    Hank Childs, Wed Oct 15 09:45:16 PDT 2003
//    Added ability to save out images in stereo.
//
//    Hank Childs, Tue Dec 16 10:58:20 PST 2003
//    Take more care in creating filenames for stereo images in subdirectories.
//
//    Mark C. Miller, Mon Mar 29 20:25:49 PST 2004
//    Changed 'Saving...' status message to indicate that its rendering.
//    Added new status message for saving the image to disk
//
//    Hank Childs, Tue Apr  6 18:05:39 PDT 2004
//    Do not save out empty data objects.
//
//    Brad Whitlock, Thu Jul 15 16:27:15 PST 2004
//    I made it send a different message for tiled images.
//
//    Brad Whitlock, Fri Jul 30 16:23:09 PST 2004
//    I added code to figure out the basename for the filenames when an
//    output directory is specified.
//
//    Hank Childs, Wed Feb 16 07:16:56 PST 2005
//    If we can't save the file, the returned filename is NULL.  Handle this
//    gracefully.
//
//    Brad Whitlock, Wed Feb 23 17:19:41 PST 2005
//    Added a call to ClearStatus.
//
//    Mark C. Miller, Tue May  3 21:49:22 PDT 2005
//    Added error check for attempts to save curve formats from windows
//    in SR mode
//
//    Mark C. Miller, Tue Mar  7 10:31:34 PST 2006
//    Made it set LastRealFileName to something bogus in case of error
//
//    Jeremy Meredith, Thu Apr  5 17:28:39 EDT 2007
//    Only try to merge the parallel domains if the user has requested it.
//    Merging two different plots (like a pseudcolor and mesh plot)
//    can cause it to drop data arrays, and many file types correctly
//    save multiple chunk data sets anyway.
//
//    Dave Bremer, Fri Sep 28 17:13:56 PDT 2007
//    Added support for a new SaveWindow option.  The user can request
//    that the window be saved with a width they specify, and the same
//    proportions that their window currently has.
//
//    Dave Bremer, Thu Oct 11 18:56:56 PDT 2007
//    Added a check for windows larger than the max Mesa can handle, and
//    reduced the resolution proportionally.
//
//    Brad Whitlock, Wed Apr 30 09:46:59 PDT 2008
//    Support for internationalization.
//
//    Jeremy Meredith, Tue Jun 24 12:27:54 EDT 2008
//    Use the actual OSMesa size limit for the window limit.
//
//    Hank Childs, Thu Jul 22 09:55:03 PDT 2010
//    Added support for advanced multi-window saves.
//
//    Hank Childs, Sun Oct 17 12:25:00 PDT 2010
//    When doing screen proportions, get the size of the rendering area, not
//    the size of the window.
//
//    Kathleen Biagas, Tue Sep 18 13:32:27 MST 2012
//    On Windows, don't append fileBase and filename if filename already
//    contains a path.  Occurs with test-suite saves.
//
//    Brad Whitlock, Fri Sep 19 10:41:47 PDT 2014
//    Add a check for master process. If we use this code in situ then we don't
//    want each rank writing a file.
//
//    Eric Brugger, Thu Sep 10 13:49:19 PDT 2015
//    Modified the routine to take into account the save tiled and advanced
//    multi window save settings when setting the image width and height.
//
//    Brad Whitlock, Tue Mar 22 19:35:40 PDT 2016
//    Do not check permissions in CreateFilename when running in situ.
//
//    Brad Whitlock, Wed Sep 20 17:50:45 PDT 2017
//    I added support for saving images with the z buffer and alpha.
//
//    Kathleen Biagas, Fri Aug 31 13:15:56 PDT 2018
//    Send Options from SaveWindowAttributes to fileWriter if non-image.
//
//    Eric Brugger, Wed Nov 13 15:40:54 PST 2024
//    I added logic so that the maximum image size is limited to 536,756,224
//    pixels.
//
// ****************************************************************************

void
ViewerWindowManager::SaveWindow(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
    {
        debug1 << "Invalid window index " << windowIndex << " of "
               << maxWindows << " was specified." << endl;
        return;
    }

    TRY
    {
        // If an image was returned, save it to the appropriate location.
        fileWriter->SetFormat(GetViewerState()->GetSaveWindowAttributes()->GetFormat());
    }
    CATCH(ImproperUseException)
    {
        GetViewerMessaging()->Error(TR("VisIt cannot save images in the specified file format."));
        CATCH_RETURN(1);
    }
    ENDTRY

    //
    // Figure out a candidate for the base filename. This involves
    // gluing the output directory on when necessary.
    //
    int t0 = visitTimer->StartTimer();
    std::string fileBase;
    if(GetViewerState()->GetSaveWindowAttributes()->GetOutputToCurrentDirectory())
    {
        fileBase = GetViewerState()->GetSaveWindowAttributes()->GetFileName();
    }
    else
    {
        fileBase = GetViewerState()->GetSaveWindowAttributes()->GetOutputDirectory();
        if(fileBase.size() > 0)
        {
            std::string f(GetViewerState()->GetSaveWindowAttributes()->GetFileName());
            if(f.size() == 0)
            {
                f = "visit";
                GetViewerMessaging()->Warning(
                    TR("The specified filename was empty. VisIt will "
                       "use the name \"visit\" as the base for the files "
                       "to be saved."));
            }
#ifdef _WIN32
            else
            {
                // need to check if our filename contains a path:
                std::string f_base = FileFunctions::Basename(f);
                if (f_base != f)
                {
                    fileBase = FileFunctions::Dirname(f);
                    f = f_base;
                }
            }
#endif
            if(fileBase[fileBase.size() - 1] == VISIT_SLASH_CHAR)
            {
                if(f[0] == VISIT_SLASH_CHAR)
                    fileBase = fileBase.substr(0,fileBase.size()-1) + f;
                else
                    fileBase += f;
            }
            else
            {
                if(f[0] == VISIT_SLASH_CHAR)
                    fileBase += f;
                else
                    fileBase = fileBase + std::string(VISIT_SLASH_STRING) + f;
            }
        }
        else
            fileBase = GetViewerState()->GetSaveWindowAttributes()->GetFileName();
    }

    //
    // We need to get a file name.  If we are in stereo, then we will need
    // two file names.
    //
    char *filename = NULL;
    char *filename2 = NULL;
    if (GetViewerState()->GetSaveWindowAttributes()->CurrentFormatIsImageFormat() &&
        GetViewerState()->GetSaveWindowAttributes()->GetStereo())
    {
        if (GetViewerState()->GetSaveWindowAttributes()->GetFamily())
        {
            filename = fileWriter->CreateFilename(fileBase.c_str(),
                                  GetViewerState()->GetSaveWindowAttributes()->GetFamily(),
                                  !GetViewerProperties()->GetInSitu());
            filename2 = fileWriter->CreateFilename(fileBase.c_str(),
                                  GetViewerState()->GetSaveWindowAttributes()->GetFamily(),
                                  !GetViewerProperties()->GetInSitu());
        }
        else
        {
            //
            // Find everything up until the last slash.
            //
            const char *fname = fileBase.c_str();
            const char *tmp = fname, *last = NULL;
            while (tmp != NULL)
            {
                tmp = strstr(tmp, VISIT_SLASH_STRING);
                if (tmp != NULL)
                {
                    last = tmp;
                    tmp = tmp+1;
                }
            }

            //
            // Now construct the string into directory and filename portions.
            //
            char dir_prefix[1024];
            char stem[1024];
            bool has_dir_prefix = false;
            if (last != NULL)
            {
                strncpy(dir_prefix, fname, last-fname);
                dir_prefix[last-fname] = '\0';
                strcpy(stem, last+1);
                has_dir_prefix = true;
            }
            else
            {
                strcpy(stem, fname);
            }

            //
            // Construct the string depending on whether or not there is a dir.
            //
            char left_prefix[1024];
            char right_prefix[1024];
            if (has_dir_prefix)
            {
                sprintf(left_prefix, "%s%cleft_%s", dir_prefix, VISIT_SLASH_CHAR,
                                                    stem);
                sprintf(right_prefix, "%s%cright_%s", dir_prefix,
                                                      VISIT_SLASH_CHAR, stem);
            }
            else
            {
                sprintf(left_prefix, "left_%s", stem);
                sprintf(right_prefix, "right_%s", stem);
            }
            filename = fileWriter->CreateFilename(left_prefix,
                                  GetViewerState()->GetSaveWindowAttributes()->GetFamily(),
                                  !GetViewerProperties()->GetInSitu());
            filename2 = fileWriter->CreateFilename(right_prefix,
                                  GetViewerState()->GetSaveWindowAttributes()->GetFamily(),
                                  !GetViewerProperties()->GetInSitu());
        }
    }
    else
    {
        filename = fileWriter->CreateFilename(fileBase.c_str(),
                              GetViewerState()->GetSaveWindowAttributes()->GetFamily(),
                              !GetViewerProperties()->GetInSitu());
        fileWriter->SetOptions(GetViewerState()->GetSaveWindowAttributes()->GetOpts());
    }

    //
    // Send a status message about starting to render the image and make the
    // status message display for 10 minutes.
    //
    ViewerText message;
    int  iCurrWindow = (windowIndex == -1) ? activeWindow : windowIndex;
    if(GetViewerState()->GetSaveWindowAttributes()->GetSaveTiled())
    {
        message = TR("Saving tiled image...");
    }
    else
    {
        message = TR("Rendering window %1...").arg(iCurrWindow+1);
    }
    GetViewerMessaging()->Status(message, 6000000);
    GetViewerMessaging()->Message(message);
    visitTimer->StopTimer(t0, "Creating filename");

    avtDataObject_p dob = NULL;
    avtDataObject_p dob2 = NULL;
    bool savedWindow = true;
    if (GetViewerState()->GetSaveWindowAttributes()->CurrentFormatIsImageFormat())
    {
        StackTimer t1("Getting image");

        avtImage_p image = NULL;
        avtImage_p image2 = NULL;

        TRY
        {
            // Set the width and height.
            int w, h;
            if (GetViewerState()->GetSaveWindowAttributes()->GetSaveTiled())
            {
                w = GetViewerState()->GetSaveWindowAttributes()->GetWidth();
                h = GetViewerState()->GetSaveWindowAttributes()->GetWidth();
            }
            else if (GetViewerState()->GetSaveWindowAttributes()->GetAdvancedMultiWindowSave())
            {
                w = GetViewerState()->GetSaveWindowAttributes()->GetWidth();
                h = GetViewerState()->GetSaveWindowAttributes()->GetHeight();
            }
            else
            {
                w = GetViewerState()->GetSaveWindowAttributes()->GetWidth();
                h = GetViewerState()->GetSaveWindowAttributes()->GetHeight();

                if (GetViewerState()->GetSaveWindowAttributes()->GetResConstraint() ==
                    SaveWindowAttributes::ScreenProportions)
                {
                    int winx, winy;
                    windows[iCurrWindow]->GetSize(winx, winy);

                    h = (int)((double)w * (double)winy / (double)winx);
                }
                else if (GetViewerState()->GetSaveWindowAttributes()->GetResConstraint() ==
                    SaveWindowAttributes::EqualWidthHeight)
                {
                    h = w;
                }
            }

            // if w or h are greated than the max window size,
            // reduce them proportionally
            if (w >= h && w > VISIT_RENDERING_SIZE_LIMIT)
            {
                h = (int)((double)h * (double)VISIT_RENDERING_SIZE_LIMIT / (double)w);
                w = VISIT_RENDERING_SIZE_LIMIT;
                GetViewerMessaging()->Message(
                    TR("The window was too large to save at the requested resolution.  "
                       "The resolution has been automatically reduced."));
            }
            else if (h >= w && h > VISIT_RENDERING_SIZE_LIMIT)
            {
                w = (int)((double)w * (double)VISIT_RENDERING_SIZE_LIMIT / (double)h);
                h = VISIT_RENDERING_SIZE_LIMIT;
                GetViewerMessaging()->Message(
                    TR("The window was too large to save at the requested resolution.  "
                       "The resolution has been automatically reduced."));
            }
	    // if the total image size is too large, reduce them
	    // proportionally.
	    if (w * h > 536756224)
            {
                double image_size = (double)(w * h);
                w = (int)((double)w * std::sqrt(536756224. / image_size));
                h = (int)((double)h * std::sqrt(536756224. / image_size));
                GetViewerMessaging()->Message(
                    TR("The window was too large to save at the requested resolution.  "
                       "The resolution has been automatically reduced."));
            }

            if (GetViewerState()->GetSaveWindowAttributes()->GetSaveTiled())
            {
                bool doAlpha = GetViewerState()->GetSaveWindowAttributes()->GetPixelData() &
                               SaveWindowAttributes::ColorRGBA;

                // Create a tiled image for the left eye.
                image = CreateTiledImage(w, h, true, doAlpha);

                // Create a tiled image for the right eye.
                if (GetViewerState()->GetSaveWindowAttributes()->GetStereo())
                {
                    image2 = CreateTiledImage(w, h, false, doAlpha);
                }
            }
            else if (GetViewerState()->GetSaveWindowAttributes()->GetAdvancedMultiWindowSave())
            {
                // Do an advanced multi-window save for the left eye.
                image = AdvancedMultiWindowSave(w, h, true);

                // Do an advanced multi-window save for the right eye.
                if (GetViewerState()->GetSaveWindowAttributes()->GetStereo())
                {
                    image2 = AdvancedMultiWindowSave(w, h, false);
                }
            }
            else
            {
                // Create the left eye.
                image = CreateSingleImage(GetViewerState()->GetSaveWindowAttributes()->GetPixelData(),
                                          (windowIndex == -1) ? activeWindow : windowIndex, w, h,
                                          GetViewerState()->GetSaveWindowAttributes()->GetScreenCapture(),
                                          true);

                // Create the right eye.
                if (GetViewerState()->GetSaveWindowAttributes()->GetStereo())
                {
                    image2 = CreateSingleImage(GetViewerState()->GetSaveWindowAttributes()->GetPixelData(),
                                               (windowIndex == -1) ? activeWindow : windowIndex, w, h,
                                               GetViewerState()->GetSaveWindowAttributes()->GetScreenCapture(),
                                               false);
                }

                if(*image == NULL)
                {
                    GetViewerMessaging()->Error(TR("No image was saved. The combination "
                        "of pixel data flags resulted in no image."));
                    return;
                }
            }
            CopyTo(dob, image);
            CopyTo(dob2, image2);
        }
        CATCH2(VisItException, ve)
        {
            GetViewerMessaging()->Warning(ve.Message().c_str());
            GetViewerMessaging()->ClearStatus();
            savedWindow = false;
        }
        ENDTRY
    }
    else
    {
        bool windowIsInScalableRenderingMode;
        avtDataset_p ds = GetDataset(windowIndex,
                                     windowIsInScalableRenderingMode);

        if (windowIsInScalableRenderingMode)
        {
            GetViewerMessaging()->Error(
                TR("You cannot save non-image formats (e.g. ultra, curve, stl, etc.) from "
                   "a window that is currently in scalable rendering mode. You may force "
                   "scalable rendering to Never but if the resulting data is too big for the "
                   "viewer to handle, it will likely crash VisIt. For 3D formats, try "
                   "an export database operation instead."));
            return;
        }

        if (GetViewerState()->GetSaveWindowAttributes()->GetForceMerge())
        {
            if (*ds != NULL)
                ds->Compact();
        }
        CopyTo(dob, ds);
    }

    //
    // Send a status message about starting to save the image to disk
    // and make the status message display for 10 minutes.
    //
    if(!GetViewerState()->GetSaveWindowAttributes()->GetSaveTiled())
    {
        message = TR("Saving window %1...").
                  arg((windowIndex == -1) ? (activeWindow + 1) : (windowIndex + 1));
        GetViewerMessaging()->Status(message, 6000000);
        GetViewerMessaging()->Message(message);
    }

    // Save the window.
    std::vector<std::string> savedFileNames;
    if(GetViewerProperties()->GetMasterProcess())
    {
        StackTimer t2("Writing image");
        if (*dob != NULL)
        {
            TRY
            {
                if (filename != NULL)
                {
                    // Tell the writer to save the window on the viewer.
                    savedFileNames = fileWriter->Write(filename, dob,
                        GetViewerState()->GetSaveWindowAttributes()->GetQuality(),
                        GetViewerState()->GetSaveWindowAttributes()->GetProgressive(),
                        GetViewerState()->GetSaveWindowAttributes()->GetCompression(),
                        GetViewerState()->GetSaveWindowAttributes()->GetBinary());

                    if (*dob2 != NULL)
                    {
                        // Tell the writer to save the window on the viewer.
                        std::vector<std::string> rFileNames;
                        rFileNames = fileWriter->Write(filename2,
                            dob2,GetViewerState()->GetSaveWindowAttributes()->GetQuality(),
                            GetViewerState()->GetSaveWindowAttributes()->GetProgressive(),
                            GetViewerState()->GetSaveWindowAttributes()->GetCompression(),
                            GetViewerState()->GetSaveWindowAttributes()->GetBinary());
                        for(size_t q = 0; q < rFileNames.size(); ++q)
                            savedFileNames.push_back(rFileNames[q]);
                    }
                }
            }
            CATCH2(VisItException, ve)
            {
                GetViewerMessaging()->Warning(ve.Message());
                GetViewerMessaging()->ClearStatus();
                savedWindow = false;
            }
            ENDTRY
        }
        else
        {
            if (GetViewerState()->GetSaveWindowAttributes()->CurrentFormatIsImageFormat())
            {
                GetViewerMessaging()->Warning(TR("No image was saved.  This is "
                      "frequently because you have asked to save an empty window."
                      "  If this is not the case, please contact a VisIt "
                      "developer."));
            }
            else
            {
                GetViewerMessaging()->Warning(TR("No surface was saved.  This is "
                      "frequently because you have asked to save an empty window."
                      "  This also happens if you are in Scalable Rendering mode."
                      "  If this is not the case, please contact a VisIt "
                      "developer.\n\n\n"
                      "If you are in scalable rendering mode and want to save a "
                      "polygonal file, go to Options->Rendering to disable this "
                      "mode.  This may cause VisIt to slow down substantially."));
            }
            GetViewerMessaging()->ClearStatus();
            savedWindow = false;
        }
    }

    // Send a message to indicate that we're done saving the image.
    if (savedWindow && filename != NULL && !savedFileNames.empty())
    {
        std::string fnList(savedFileNames[0]);
        for(size_t q = 1; q < savedFileNames.size(); ++q)
            fnList = (fnList + ", ") + savedFileNames[q];

        message = TR("Saved %1").arg(fnList);
        GetViewerMessaging()->Status(message);
        GetViewerMessaging()->Message(message);
        GetViewerState()->GetSaveWindowAttributes()->SetLastRealFilename(filename);
    }
    else
    {
        message = TR("Could not save window");
        GetViewerMessaging()->Status(message);
        GetViewerMessaging()->Message(message);

        // specify an impossible filename to have saved
        GetViewerState()->GetSaveWindowAttributes()->SetLastRealFilename("/dev/null/SaveWindow_Error.txt");
    }
    GetViewerState()->GetSaveWindowAttributes()->Notify();

    // Delete the filename memory.
    if (filename != NULL)
        delete [] filename;
    if (filename2 != NULL)
        delete [] filename2;
}

// ****************************************************************************
//  Method: ViewerWindowManager::CreateSingleImage
//
//  Purpose:
//    Returns an avtImage representation of the VisWindow. The image may have
//    resulted from multiple renders.
//
//  Arguments:
//    pixelType      The types of pixels we want to obtain.
//    windowIndex    The window index for which we want an image.
//    width          The desired width of the return image.
//    height         The desired height of the return image.
//    screenCapture  A flag indicating whether or not to do screen capture.
//    leftEye        True if we want the left eye.
//
//  Returns:    An avtImage representation of the specified VisWindow.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Sep 28 10:08:25 PDT 2017
//
//  Modifications:
//
// ****************************************************************************

//#define CREATE_SINGLE_IMAGE_DEBUG
#ifdef CREATE_SINGLE_IMAGE_DEBUG
#include <vtkPNGWriter.h>
#include <vtkFloatArray.h>
#endif

avtImage_p
ViewerWindowManager::CreateSingleImage(int pixelData, int windowIndex,
    int width, int height, bool screenCapture, bool leftEye)
{
    const char *mName = "ViewerWindowManager::CreateSingleImage: ";
    avtImage_p retval = NULL;
    int nSrcImages = 0;
    bool haveZ = false;
    bool append = false;

    // Do not allow screen capture of we're making multiple images.
    bool doScreenCapture = screenCapture;
    if((pixelData & SaveWindowAttributes::Luminance) ||
       (pixelData & SaveWindowAttributes::Value)
      )
    {
        // If we'd otherwise screen capture then get the SC window size.
        if(screenCapture)
            windows[windowIndex]->GetSize(width, height);
        doScreenCapture = false;
        debug5 << mName << "Window size: width=" << width << ", height=" << height << endl;
    }

    // Get the color data.
    if((pixelData & SaveWindowAttributes::ColorRGB) ||
       (pixelData & SaveWindowAttributes::ColorRGBA)
      )
    {
        bool doAlpha = (pixelData & SaveWindowAttributes::ColorRGBA) &&
                       GetViewerState()->GetSaveWindowAttributes()->CurrentFormatSupportsAlpha();
        bool doZBuffer = pixelData & SaveWindowAttributes::Depth;

        debug5 << mName << "Request RGB" << (doAlpha?"A":"") << " image. doZBuffer=" << doZBuffer << endl;

        retval = CreateSingleImageType(
            doAlpha ? ColorRGBAImage : ColorRGBImage, doZBuffer,
            windowIndex, width, height, doScreenCapture, leftEye);

#ifdef CREATE_SINGLE_IMAGE_DEBUG
        vtkPNGWriter *writer = vtkPNGWriter::New();
        writer->SetFileName("csi_rgb.png");
        writer->SetInputData(retval->GetImage().GetImageVTK());
        writer->Write();
        writer->Delete();
#endif
        haveZ = true;
        append = true;
    }

    // Get the luminance data
    if(pixelData & SaveWindowAttributes::Luminance)
    {
        bool doZBuffer = false;
        bool doAlpha = false;
        if(!haveZ)
            doZBuffer = pixelData & SaveWindowAttributes::Depth;

        debug5 << "Request Luminance image. doZBuffer=" << doZBuffer << endl;

        avtImage_p luminance = CreateSingleImageType(
            LuminanceImage, doZBuffer,
            windowIndex, width, height, doScreenCapture, leftEye);

#ifdef CREATE_SINGLE_IMAGE_DEBUG
        vtkPNGWriter *writer = vtkPNGWriter::New();
        writer->SetFileName("csi_lum.png");
        writer->SetInputData(luminance->GetImage().GetImageVTK());
        writer->Write();
        writer->Delete();
#endif

        if(append)
        {
            // Get current scalars for the RGB image
            vtkDataArray *scalars = retval->GetImage().GetImageVTK()->GetPointData()->GetScalars();
            debug5 << mName << "retval scalars = " << (void*)scalars
                   << " name=" << (scalars?scalars->GetName():"NULL") << endl;

            // Add the luminance image into the VTK image data.
            vtkUnsignedCharArray *L = luminance->GetImage().GetRGBBufferVTK();
            if(L != NULL)
            {
                debug5 << mName << "Renaming " << L->GetName() << " to luminance and adding to retval image." << endl;
                L->SetName("luminance");
                retval->GetImage().GetImageVTK()->GetPointData()->AddArray(L);

                // Now that we've added the luminance data, restore the scalar in retval.
                if(scalars != NULL)
                {
                    debug5 << mName << "Resetting the scalars to " << (void*)scalars
                           << " name=" << scalars->GetName() << endl;
                    retval->GetImage().GetImageVTK()->GetPointData()->SetScalars(scalars);
                }
            }
            else
            {
                debug5 << mName << "We could not get the luminance pixels." << endl;
            }

            if(!haveZ && doZBuffer)
            {
                // Take the image's zbuffer and store a reference into retval.
                retval->GetImage().SetZBufferVTK(luminance->GetImage().GetZBufferVTK());
                haveZ = true;
            }
        }
        else
        {
            retval = luminance;
            if(!haveZ)
                haveZ = doZBuffer;
        }

        append = true;
    }

    // Get the value data.
    if(pixelData & SaveWindowAttributes::Value)
    {
        bool doZBuffer = false;
        if(!haveZ)
            doZBuffer = pixelData & SaveWindowAttributes::Depth;

        debug5 << mName << "Request Value image. doZBuffer=" << doZBuffer << endl;

        avtImage_p value = CreateSingleImageType(
             ValueImage, doZBuffer,
             windowIndex, width, height, doScreenCapture, leftEye);

        if(append && *value != NULL)
        {
            // Get the scalars for the return image.
            vtkDataArray *scalars = retval->GetImage().GetImageVTK()->GetPointData()->GetScalars();
            debug5 << mName << "retval scalars = " << (void*)scalars
                   << " name=" << (scalars?scalars->GetName():"NULL") << endl;

            // Add the value image into the VTK image data.
            vtkDataArray *v = value->GetImage().GetImageVTK()->GetPointData()->GetScalars();
            if(v != NULL)
            {
                debug5 << mName << "Renaming " << v->GetName() << " to value and adding to retval image." << endl;
                v->SetName("value");
                retval->GetImage().GetImageVTK()->GetPointData()->AddArray(v);

                // Now that we've added the luminance data, restore the scalar in retval.
                if(scalars != NULL)
                {
                    debug5 << mName << "Resetting the scalars to " << (void*)scalars
                           << " name=" << scalars->GetName() << endl;
                    retval->GetImage().GetImageVTK()->GetPointData()->SetScalars(scalars);
                }
            }
        }
        else
        {
            retval = value;
        }
    }

#ifdef CREATE_SINGLE_IMAGE_DEBUG
    // Let's see what we made.
    if(*retval != NULL)
    {
        debug5 << mName;
        for(int i = 0; i < retval->GetImage().GetImageVTK()->GetPointData()->GetNumberOfArrays(); ++i)
        {
            debug5 << retval->GetImage().GetImageVTK()->GetPointData()->GetArray(i)->GetName() << ", ";
        }
        if(retval->GetImage().GetZBufferVTK() != NULL)
        {
            debug5 << "zbuffer = " << retval->GetImage().GetZBufferVTK()->GetName();
        }
        else
        {
            debug5 << "zbuffer = NULL";
        }
        debug5 << endl;
    }
#endif

    return retval;
}

// ****************************************************************************
//  Method: ViewerWindowManager::CreateSingleImageType
//
//  Purpose:
//    Returns an avtImage representation of the VisWindow.
//
//  Arguments:
//    windowIndex    The window index for which we want an image.
//    width          The desired width of the return image.
//    height         The desired height of the return image.
//    screenCapture  A flag indicating whether or not to do screen capture.
//    leftEye        True if we want the left eye.
//
//  Returns:    An avtImage representation of the specified VisWindow.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Feb 13 15:17:32 PST 2001
//
//  Modifications:
//
//    Hank Childs, Wed Oct 15 10:30:33 PDT 2003
//    Added stereo support.
//
//    Mark C. Miller, Mon Mar 29 14:05:23 PST 2004
//    Enabled non-screen-capture based mode as well as width and height
//    arguments
//
//    Mark C. Miller, Tue Mar  7 19:43:56 PST 2006
//    Added code to refuse to save and warn user if non-screen-capture saves
//    are attempted with animation caching turned on.
//
//    Eric Brugger, Thu Mar 22 12:53:34 PDT 2007
//    Modified so that it doesn't use screen capture when in nowin mode.
//
//    Eric Brugger, Tue Mar 27 12:12:55 PDT 2007
//    Modified so that the screen capture behavior depends on whether it is
//    using the mesa stub library or not.
//
//    Jeremy Meredith, Fri Jan  4 13:27:37 EST 2008
//    Added a warning about attempting to save when nowin mode and screen
//    capture are enabled, but Mesa has been stubbed out.  There was a
//    previous warning when animation caching was also enabled, but this
//    wasn't broad enough.
//
//    Brad Whitlock, Wed Apr 30 09:50:48 PDT 2008
//    Support for internationalization.
//
//    Brad Whitlock, Wed Dec 10 14:50:55 PST 2008
//    Get animation attributes directly from the plot list.
//
//    Brad Whitlock, Wed Sep 20 17:47:53 PDT 2017
//    Added imgT and doZBuffer.
//
// ****************************************************************************

avtImage_p
ViewerWindowManager::CreateSingleImageType(avtImageType imgT, bool doZBuffer,
    int windowIndex,  int width, int height, bool screenCapture, bool leftEye)
{
    int        index = (windowIndex == -1) ? activeWindow : windowIndex;
    avtImage_p retval = NULL;

    if(windows[index] != 0)
    {
        if (!leftEye)
        {
            windows[index]->ConvertFromLeftEyeToRightEye();
        }

        if(screenCapture && (imgT == ColorRGBImage || imgT == ColorRGBAImage))
        {
            StackTimer t0("Screen capture");
#ifdef MESA_STUB
            if(GetViewerProperties()->GetNowin() == false)
            {
                retval = windows[index]->ScreenCapture(imgT, doZBuffer);
            }
            else
            {
                GetViewerMessaging()->Warning(
                    TR("Currently, you cannot save images when in nowin"
                       " mode using screen capture and Mesa has been"
                       " stubbed out in the viewer.  Either disable"
                       " screen capture, or rebuild without the Mesa"
                       " stub library.  Note that the Mesa stub"
                       " library was in place to prevent compatibility"
                       " problems with some graphics drivers."));
            }
#else
            retval = windows[index]->ScreenCapture(imgT, doZBuffer);
#endif
        }
        else
        {
            StackTimer t1("External Render");
            if (windows[index]->GetPlotList()->GetAnimationAttributes().GetPipelineCachingMode())
            {
                GetViewerMessaging()->Warning(
                    TR("Currently, you cannot use non-screen-capture mode saves "
                       "when you have animation caching turned on. Either turn off "
                       "animation caching or use screen capture to save your windows."));
            }
            else
            {
                avtDataObject_p extImage;
                windows[index]->ExternalRenderManual(extImage, width, height,
                                                     imgT, doZBuffer);
                CopyTo(retval, extImage);
            }
        }

        if (!leftEye)
        {
            windows[index]->ConvertFromRightEyeToLeftEye();
        }
    }

    return retval;
}

// ****************************************************************************
//  Method: ViewerWindowManager::CreateTiledImage
//
//  Purpose:
//    This method returns a tiled image of all of the open ViewerWindows.
//
//  Arguments:
//    width     The desired width of the tiled image.
//    height    The desired height of the tiled image.
//    leftEye   Whether we render for the left eye.
//    dpAlpha   Whether we should make images with alpha.
//
//  Returns:    A tiled image.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Feb 13 15:27:26 PST 2001
//
//  Modifications:
//    Brad Whitlock, Thu Jul 15 13:38:37 PST 2004
//    Implemented the method finally.
//
//    Brad Whitlock, Wed Apr 30 09:51:28 PDT 2008
//    Support for internationalization.
//
// ****************************************************************************

avtImage_p
ViewerWindowManager::CreateTiledImage(int width, int height, bool leftEye, bool doAlpha)
{
    bool doZBuffer = false;
    avtImageType imgT = doAlpha ? ColorRGBAImage : ColorRGBImage;

    //
    // Determine how many windows actually have plots to save in the
    // tiled image. Also sort the windows into the sortedWindows array.
    // We need to do this because if any windows have ever been deleted,
    // new windows can appear in the first unused slot in the windows array.
    // Thus the windows array does not contain windows in any given order.
    // Since we want to always tile the image so that windows are in order
    // (1, 2, 3, ...) we sort.
    //
    int windowIndex, windowWithPlot = -1, windowsWithPlots = 0;
    ViewerWindow **sortedWindows = new ViewerWindow*[maxWindows];
    for(windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
        sortedWindows[windowIndex] = 0;
    for(windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        ViewerWindow *win = windows[windowIndex];
        if(win != 0 && win->GetPlotList()->GetNumPlots() > 0)
        {
            sortedWindows[win->GetWindowId()] = win;
            if(windowWithPlot == -1)
                windowWithPlot = windowIndex;
            ++windowsWithPlots;
        }
    }

    //
    // Return early if none of the windows have plots.
    //
    if(windowsWithPlots == 0)
    {
        delete [] sortedWindows;
        GetViewerMessaging()->Warning(
            TR("VisIt did not save a tiled image because none of the "
               "windows had any plots."));
        return NULL;
    }
    else if(windowsWithPlots == 1)
    {
        delete [] sortedWindows;
        // There's just 1 window that has plots don't bother tiling.
        return CreateSingleImageType(imgT, doZBuffer, windowWithPlot,
                width, height,
                GetViewerState()->GetSaveWindowAttributes()->GetScreenCapture(),
                leftEye);
    }

    //
    // If we're not in screen capture mode then divide up the prescribed
    // image size among the tiles that we have.
    //
    avtImageTiler tiler(windowsWithPlots);

    int imageWidth = width;
    int imageHeight = height;
    if(!GetViewerState()->GetSaveWindowAttributes()->GetScreenCapture())
    {
        imageWidth = width / tiler.GetNumberOfColumnsForNTiles(windowsWithPlots);
        imageHeight = imageWidth;
    }

    //
    // Get an image for each window that has plots and add the images to the
    // tiler object.
    //
    for(int index = 0; index < maxWindows; ++index)
    {
        if(sortedWindows[index] != 0)
        {
            tiler.AddImage(CreateSingleImageType(imgT, doZBuffer,
                sortedWindows[index]->GetWindowId(),
                imageWidth, imageHeight,
                GetViewerState()->GetSaveWindowAttributes()->GetScreenCapture(), leftEye));
            GetViewerMessaging()->Message(TR("Saving tiled image..."));
        }
    }
    delete [] sortedWindows;

    //
    // Return the tiled image returned by the tiler.
    //
    return tiler.CreateTiledImage();
}

// ****************************************************************************
//  Method: ViewerWindowManager::AdvancedMultiWindowSave
//
//  Purpose:
//    This method does an advanced multi-window save.
//
//  Arguments:
//    width     The desired width of the tiled image.
//    height    The desired height of the tiled image.
//    leftEye   True if this is for the left eye.
//
//  Returns:    An image containing results from multiple windows.
//
//  Programmer: Hank Childs
//  Creation:   July 16, 2010
//
// ****************************************************************************

avtImage_p
ViewerWindowManager::AdvancedMultiWindowSave(int width, int height,
                                             bool leftEye)
{
    //
    // Determine which windows actually have plots to save.
    // Also sort the windows into the sortedWindows array.
    // We need to do this because if any windows have ever been deleted,
    // new windows can appear in the first unused slot in the windows array.
    // Thus the windows array does not contain windows in any given order.
    // Since we want to always tile the image so that windows are in order
    // (1, 2, 3, ...) we sort.
    //
    int windowIndex, windowWithPlot = -1, windowsWithPlots = 0;
    ViewerWindow **sortedWindows = new ViewerWindow*[maxWindows];
    for(windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
        sortedWindows[windowIndex] = 0;
    for(windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        ViewerWindow *win = windows[windowIndex];
        if(win != 0 && win->GetPlotList()->GetNumPlots() > 0)
        {
            sortedWindows[win->GetWindowId()] = win;
            if(windowWithPlot == -1)
                windowWithPlot = windowIndex;
            ++windowsWithPlots;
        }
    }

    //
    // Return early if none of the windows have plots.
    //
    if(windowsWithPlots == 0)
    {
        delete [] sortedWindows;
        GetViewerMessaging()->Warning(
            TR("VisIt did not do an advanced window save because none of "
               "the windows had any plots."));
        return NULL;
    }

    //
    // If we're not in screen capture mode then divide up the prescribed
    // image size among the tiles that we have.
    //
    avtMultiWindowSaver mws(GetViewerState()->GetSaveWindowAttributes()->GetSubWindowAtts());
    mws.SetImageSize(width, height);

    //
    // Get an image for each window that has plots and add the images to the
    // multi-window-saver (mws) object.
    //
    for(int index = 0; index < maxWindows; ++index)
    {
        if(sortedWindows[index] != 0)
        {
            bool doScreenCapture = false;
            bool doZBuffer = false;
            avtImageType imgT = ColorRGBImage;
            int  winId = sortedWindows[index]->GetWindowId();
            SaveSubWindowsAttributes &atts =
                                      GetViewerState()->GetSaveWindowAttributes()->GetSubWindowAtts();
            SaveSubWindowAttributes winAtts = atts.GetAttsForWindow(winId+1);
            if (winAtts.GetOmitWindow())
                continue;
            const int *size = winAtts.GetSize();
            mws.AddImage(CreateSingleImageType(
                imgT, doZBuffer, winId,
                size[0], size[1],
                doScreenCapture, leftEye), winId+1);
            GetViewerMessaging()->Message(TR("Saving tiled image..."));
        }
    }
    delete [] sortedWindows;

    //
    // Return the tiled image returned by the tiler.
    //
    return mws.CreateImage();
}

// ****************************************************************************
// Method: ViewerWindowManager::GetDataset
//
// Purpose:
//   Goes to the vis window and gets a mesh that comprises all of the plots in
//   the window and makes an attempt at setting up their colors.
//
// Programmer: Hank Childs
// Creation:   May 24, 2002
//
// Modifications:
//
//   Mark C. Miller, Tue May  3 21:49:22 PDT 2005
//   Added windowIsInScalableRenderingMode
//
// ****************************************************************************

avtDataset_p
ViewerWindowManager::GetDataset(int windowIndex,
    bool& windowIsInScalableRenderingMode)
{
    int          index = (windowIndex == -1 ? activeWindow : windowIndex);
    avtDataset_p rv    = NULL;

    windowIsInScalableRenderingMode = windows[index]->GetScalableRendering();
    if(!windowIsInScalableRenderingMode && windows[index] != 0)
    {
        rv = windows[index]->GetAllDatasets();
    }

    return rv;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetInteractionMode
//
//  Purpose:
//    This method sets the interaction mode for the specified window.
//
//  Arguments:
//    m            The interaction mode.
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Nov 7 13:31:14 PST 2000
//
//  Modifications:
//   Brad Whitlock, Mon Sep 16 15:21:09 PST 2002
//   I made it update the WindowInformation.
//
// ****************************************************************************

void
ViewerWindowManager::SetInteractionMode(INTERACTION_MODE m,
    int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->SetInteractionMode(m);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetToolUpdateMode
//
//  Purpose:
//    This method sets the tool updte mode for the specified window.
//
//  Arguments:
//    m            The tool update mode.
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Jeremy Meredith
//  Creation:   February  1, 2010
//
//  Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetToolUpdateMode(TOOLUPDATE_MODE m, int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->SetToolUpdateMode(m);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetViewCurveFromClient
//
//  Purpose:
//    Sets the view for the active window using the client view attributes.
//
//  Programmer: Eric Brugger
//  Creation:   August 20, 2003
//
//  Modifications:
//    Kathleen Bonnell, Thu Mar 22 19:24:21 PDT 2007
//    Added support for log scaling.
//
//    Kathleen Bonnell, Wed May  9 17:40:35 PDT 2007
//    Added error message for non-positive values when log scaling.  Only
//    attempt to take the log of the domain/range coords if there are plots.
//
//    Jeremy Meredith, Mon Feb  4 13:32:04 EST 2008
//    Have the various calls to UpdateViewAtts tell the routine not to bother
//    updating the atttributes for the Axis Array window modality.
//
//    Brad Whitlock, Wed Apr 30 09:54:48 PDT 2008
//    Support for internationalization.
//
//    Mark Miller, Mon Jun 30 14:42:47 PDT 2008
//    Made the log scale error more GUI-centric and less code-centric.
//
//    Kathleen Bonnell, Thu Jul 10 16:51:12 PDT 2008
//    Allow the view to be reset to the plot limits before log scaling,
//    in case the view has inadvertently been changed to include non-positive
//    values.  Only do the non-postive test if log scaling is being turned on.
//
//    Kathleen Bonnell, Tue Mar  3 15:04:57 PST 2009
//    CanDoLogViewScaling changed to PermitsLogViewScaling.
//
// ****************************************************************************

void
ViewerWindowManager::SetViewCurveFromClient()
{
    avtViewCurve viewCurve = windows[activeWindow]->GetViewCurve();

    const double *viewport=GetViewerState()->GetViewCurveAttributes()->GetViewportCoords();
    double *domain=GetViewerState()->GetViewCurveAttributes()->GetDomainCoords();
    double *range=GetViewerState()->GetViewCurveAttributes()->GetRangeCoords();
    ScaleMode newDomainScale = (ScaleMode)GetViewerState()->GetViewCurveAttributes()->GetDomainScale();
    ScaleMode newRangeScale = (ScaleMode)GetViewerState()->GetViewCurveAttributes()->GetRangeScale();
    bool updateScaleMode = ((viewCurve.domainScale != newDomainScale) ||
                            (viewCurve.rangeScale != newRangeScale));

    if (windows[activeWindow]->GetWindowMode() == WINMODE_CURVE)
    {
        ViewerPlotList *vpl = windows[activeWindow]->GetPlotList();
        if ((newDomainScale == LOG || newRangeScale == LOG) &&
           (vpl->GetNumPlots() > 0 && !vpl->PermitsLogViewScaling(WINMODE_CURVE)))
        {
            UpdateViewAtts(activeWindow, true, false, false, false);
            GetViewerMessaging()->Error(
                TR("There are plots in the window that do not\n"
                   "support log-scaling.  It will not be done."));
            return;
        }
        if (updateScaleMode && newDomainScale == LOG &&
            newDomainScale != viewCurve.domainScale)
        {
            if (domain[0] <= 0 || domain[1] <= 0)
            {
                double lims[4];
                vpl->GetPlotLimits(2, lims);
                if (lims[0] <= 0 || lims[1] <= 0)
                {
                    UpdateViewAtts(activeWindow, true, false, false, false);
                    GetViewerMessaging()->Error(
                        TR("There are non-positive values in the domain of \n"
                           "the curve, so log scaling cannot be done. You must\n"
                           "limit the spatial extents to positive values.\n"
                           "e.g. via Transform or Box operators and/or\n"
                           "setting 'view based on' 'Original spatial extents'\n"
                           "in Controls->View->Advanced"));
                    return;
                }
                else
                {
                    domain[0] = lims[0];
                    domain[1] = lims[1];
                }
            }
        }
        if (updateScaleMode && newRangeScale == LOG &&
            newRangeScale != viewCurve.rangeScale)
        {
            if (range[0] <= 0 || range[1] <= 0)
            {
                double lims[4];
                vpl->GetPlotLimits(2, lims);
                if (lims[2] <= 0 || lims[3] <= 0)
                {
                    UpdateViewAtts(activeWindow, true, false, false, false);
                    GetViewerMessaging()->Error(
                        TR("There are non-positive values in the range of \n"
                           "the curve, so log scaling cannot be done. You must\n"
                           "limit the spatial extents to positive values.\n"
                           "e.g. via Transform or Box operators and/or\n"
                           "setting 'view based on' 'Original spatial extents'\n"
                           "in Controls->View->Advanced"));
                    return;
                }
                else
                {
                    range[0] = lims[2];
                    range[1] = lims[3];
                }
            }
        }
    }
    for (int i = 0; i < 4; i++)
    {
        viewCurve.viewport[i] = viewport[i];
    }

    viewCurve.domain[0] = domain[0];
    viewCurve.domain[1] = domain[1];
    viewCurve.range[0]  = range[0];
    viewCurve.range[1]  = range[1];
    if (windows[activeWindow]->GetPlotList()->GetNumRealizedPlots() > 0)
    {
#define SMALL 1e-100
        if (!viewCurve.havePerformedLogDomain && newDomainScale == LOG)
        {
            viewCurve.domain[0] = log10(fabs(viewCurve.domain[0]) + SMALL);
            viewCurve.domain[1] = log10(fabs(viewCurve.domain[1]) + SMALL);
            viewCurve.havePerformedLogDomain = true;
        }
        else if (viewCurve.havePerformedLogDomain && newDomainScale == LINEAR)
        {
            viewCurve.domain[0] = pow(10., viewCurve.domain[0]);
            viewCurve.domain[1] = pow(10., viewCurve.domain[1]);
            viewCurve.havePerformedLogDomain = false;
        }
        if (!viewCurve.havePerformedLogRange && newRangeScale == LOG)
        {
            viewCurve.range[0] = log10(fabs(viewCurve.range[0]) + SMALL);
            viewCurve.range[1] = log10(fabs(viewCurve.range[1]) + SMALL);
            viewCurve.havePerformedLogRange = true;
        }
        else if (viewCurve.havePerformedLogRange && newRangeScale == LINEAR)
        {
            viewCurve.range[0] = pow(10., viewCurve.range[0]);
            viewCurve.range[1] = pow(10., viewCurve.range[1]);
            viewCurve.havePerformedLogRange = false;
        }
    }

    viewCurve.domainScale = newDomainScale;
    viewCurve.rangeScale  = newRangeScale;
    if (updateScaleMode)
    {
        windows[activeWindow]->SetScaleMode(viewCurve.domainScale, viewCurve.rangeScale, WINMODE_CURVE);
    }

    //
    // Set the Curve view for the active viewer window.
    //
    windows[activeWindow]->SetViewCurve(viewCurve);

    //
    // This will maintain our internal state and also make locked windows
    // get this view.
    //
    UpdateViewAtts(activeWindow, true, false, false, false);
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetView2DFromClient
//
//  Purpose:
//    Sets the view for the active window using the client view attributes.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Jul 20 10:41:58 PDT 2001
//
//  Modifications:
//    Eric Brugger, Mon Aug 20 12:05:46 PDT 2001
//    Modify the routine to use an avtView2D to set the 2D view.
//
//    Hank Childs, Fri Oct 18 15:00:33 PDT 2002
//    Call UpdateViewAtts so that locked windows also update.
//
//    Brad Whitlock, Tue Nov 19 14:40:03 PST 2002
//    I changed UpdateViewAtts so the 3d view will not be sent to the client.
//
//    Kathleen Bonnell, Tue Jul 15 08:30:52 PDT 2003
//    Retrieve active window's 2d view, instead of instantiating a new one,
//    so that scale factor for full-frame mode is not lost during update.
//
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I changed the call to UpdateViewAtts.
//
//    Eric Brugger, Thu Oct 16 14:32:08 PDT 2003
//    I added a full frame mode to the 2d view.
//
//    Mark C. Miller, Thu Jul 21 12:52:42 PDT 2005
//    Added logic to manage auto full frame mode
//
//    Mark C. Miller, Tue Mar 14 17:49:26 PST 2006
//    Moved some parameters dealing with full frame to avtView2D
//
//    Mark C. Miller, Thu Apr  6 01:45:57 PDT 2006
//    Moved code to check axes' units to ViewerWindow.
//
//    Kathleen Bonnell, Wed May  9 17:40:35 PDT 2007
//    Added support for log scaling.
//
//    Jeremy Meredith, Mon Feb  4 13:32:04 EST 2008
//    Have the various calls to UpdateViewAtts tell the routine not to bother
//    updating the atttributes for the Axis Array window modality.
//
//    Brad Whitlock, Wed Apr 30 09:55:23 PDT 2008
//    Support for internationalization.
//
//    Mark Miller, Mon Jun 30 14:42:01 PDT 2008
//    Made the log scale error more GUI-centric and less code-centric.
//
//    Kathleen Bonnell, Thu Jul 10 16:51:12 PDT 2008
//    Allow the view to be reset to the plot limits before log scaling,
//    in case the view has inadvertently been changed to include non-positive
//    values.  Only do the non-postive test if log scaling is being turned on.
//
//    Kathleen Bonnell, Tue Mar  3 15:04:57 PST 2009
//    CanDoLogViewScaling changed to PermitsLogViewScaling.
//
// ****************************************************************************

void
ViewerWindowManager::SetView2DFromClient()
{
    avtView2D view2d = windows[activeWindow]->GetView2D();

    ScaleMode newXScale = (ScaleMode)GetViewerState()->GetView2DAttributes()->GetXScale();
    ScaleMode newYScale = (ScaleMode)GetViewerState()->GetView2DAttributes()->GetYScale();
    bool updateScaleMode = ((view2d.xScale != newXScale) ||
                            (view2d.yScale != newYScale));

    for(int i = 0; i < 4; ++i)
    {
        view2d.viewport[i] = GetViewerState()->GetView2DAttributes()->GetViewportCoords()[i];
        view2d.window[i] = GetViewerState()->GetView2DAttributes()->GetWindowCoords()[i];
    }

    if (windows[activeWindow]->GetWindowMode() == WINMODE_2D)
    {
        ViewerPlotList *vpl = windows[activeWindow]->GetPlotList();
        if ((newXScale == LOG || newYScale == LOG) &&
            (vpl->GetNumPlots() > 0 && !vpl->PermitsLogViewScaling(WINMODE_2D)))
        {
            UpdateViewAtts(activeWindow, false, true, false, false);
            GetViewerMessaging()->Error(
                 TR("There are plots in the window that do not\n"
                    "support log-scaling.  It will not be done."));
            return;
        }
        if (updateScaleMode && newXScale == LOG &&
            newXScale != view2d.xScale)
        {
            if (view2d.window[0] <= 0 || view2d.window[1] <= 0)
            {
                double lims[4];
                vpl->GetPlotLimits(2, lims);
                if (lims[0] <= 0 || lims[1] <= 0)
                {
                    UpdateViewAtts(activeWindow, false, true, false, false);
                    GetViewerMessaging()->Error(
                        TR("There are non-positive values in the x-coords \n"
                           "of the mesh, so log scaling cannot be done. You must\n"
                           "limit the spatial extents to positive values.\n"
                           "e.g. via Transform or Box operators and/or\n"
                           "setting 'view based on' 'Original spatial extents'\n"
                           "in Controls->View->Advanced"));
                    return;
                }
                else
                {
                    view2d.window[0] = lims[0];
                    view2d.window[1] = lims[1];
                }

            }
        }
        if (updateScaleMode && newYScale == LOG &&
            newYScale != view2d.yScale)
        {
            if (view2d.window[2] <= 0 || view2d.window[3] <= 0)
            {
                double lims[4];
                vpl->GetPlotLimits(2, lims);
                if (lims[2] <= 0 || lims[3] <= 0)
                {
                    UpdateViewAtts(activeWindow, false, true, false, false);
                    GetViewerMessaging()->Error(
                        TR("There are non-positive values in the y-coords\n"
                           "of the mesh, so log scaling cannot be done. You\n"
                           "must limit the spatial extents to positive values.\n"
                           "e.g. via Transform or Box operators and/or\n"
                           "setting 'view based on' 'Original spatial extents'\n"
                           "in Controls->View->Advanced"));
                    return;
                }
                else
                {
                    view2d.window[2] = lims[2];
                    view2d.window[3] = lims[3];
                }
            }
        }
    }


    view2d.fullFrameActivationMode = GetViewerState()->GetView2DAttributes()->GetFullFrameActivationMode();
    view2d.fullFrameAutoThreshold = GetViewerState()->GetView2DAttributes()->GetFullFrameAutoThreshold();
    view2d.fullFrame = GetViewerState()->GetView2DAttributes()->GetUseFullFrame();

    if (windows[activeWindow]->GetPlotList()->GetNumRealizedPlots()  > 0)
    {
#define SMALL 1e-100
        if (!view2d.havePerformedLogX  && newXScale == LOG)
        {
            view2d.window[0] = log10(fabs(view2d.window[0]) + SMALL);
            view2d.window[1] = log10(fabs(view2d.window[1]) + SMALL);
            view2d.havePerformedLogX = true;
        }
        else if (view2d.havePerformedLogX && newXScale == LINEAR)
        {
            view2d.window[0] = pow(10., view2d.window[0]);
            view2d.window[1] = pow(10., view2d.window[1]);
            view2d.havePerformedLogX = false;
        }
        if (!view2d.havePerformedLogY && newYScale == LOG)
        {
            view2d.window[2] = log10(fabs(view2d.window[2]) + SMALL);
            view2d.window[3] = log10(fabs(view2d.window[3]) + SMALL);
            view2d.havePerformedLogY = true;
        }
        else if (view2d.havePerformedLogY && newYScale == LINEAR)
        {
            view2d.window[2] = pow(10., view2d.window[2]);
            view2d.window[3] = pow(10., view2d.window[3]);
            view2d.havePerformedLogY = false;
        }
    }

    view2d.xScale = newXScale;
    view2d.yScale = newYScale;
    if (updateScaleMode)
    {
        windows[activeWindow]->SetScaleMode(view2d.xScale, view2d.yScale, WINMODE_2D);
    }


    if (GetViewerState()->GetView2DAttributes()->GetFullFrameActivationMode() ==
        View2DAttributes::Auto)
    {
        double extents[4];
        windows[activeWindow]->GetExtents(2, extents);
        bool newFullFrameMode = GetViewerState()->GetView2DAttributes()->GetUseFullFrame(extents);
        if (!windows[activeWindow]->DoAllPlotsAxesHaveSameUnits())
            newFullFrameMode = true;
        view2d.fullFrame = newFullFrameMode;
    }

    //
    // Set the 2D view for the active viewer window.
    //
    windows[activeWindow]->SetView2D(view2d);

    //
    // This will maintain our internal state and also make locked windows
    // get this view.
    //
    UpdateViewAtts(activeWindow, false, true, false, false);
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetView3DFromClient
//
//  Purpose:
//    Sets the view for the active window using the client view attributes.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Jul 20 10:41:58 PDT 2001
//
//  Modifications:
//    Eric Brugger, Mon Aug 20 12:05:46 PDT 2001
//    Modify the routine to use an avtView3D to set the 2D view.
//
//    Hank Childs, Fri Oct 18 15:00:33 PDT 2002
//    Call UpdateViewAtts so that locked windows also update.
//
//    Brad Whitlock, Tue Nov 19 14:40:03 PST 2002
//    I changed UpdateViewAtts so the 2d view will not be sent to the client.
//
//    Eric Brugger, Tue Jun 10 13:10:17 PDT 2003
//    I renamed camera to view normal in the view attributes.  I added
//    image pan and image zoom to the 3d view attributes.
//
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I changed the call to UpdateViewAtts.
//
//    Hank Childs, Wed Oct 15 12:58:19 PDT 2003
//    Copied over eye angle.
//
//    Eric Brugger, Wed Feb 11 08:52:25 PST 2004
//    Added code to copy center of rotation information.
//
//    Jeremy Meredith, Mon Feb  4 13:32:04 EST 2008
//    Have the call to UpdateViewAtts tell the routine not to bother
//    updating the atttributes for the Axis Array window modality.
//
//    Jeremy Meredith, Wed May 19 14:15:58 EDT 2010
//    Support 3D axis scaling (3D equivalent of full-frame mode).
//
//    Jeremy Meredith, Mon Aug  2 14:23:08 EDT 2010
//    Add shear for oblique projection support.
//
// ****************************************************************************

void
ViewerWindowManager::SetView3DFromClient()
{
    avtView3D view3d;

    view3d.normal[0] = GetViewerState()->GetView3DAttributes()->GetViewNormal()[0];
    view3d.normal[1] = GetViewerState()->GetView3DAttributes()->GetViewNormal()[1];
    view3d.normal[2] = GetViewerState()->GetView3DAttributes()->GetViewNormal()[2];
    view3d.focus[0] = GetViewerState()->GetView3DAttributes()->GetFocus()[0];
    view3d.focus[1] = GetViewerState()->GetView3DAttributes()->GetFocus()[1];
    view3d.focus[2] = GetViewerState()->GetView3DAttributes()->GetFocus()[2];
    view3d.viewUp[0] = GetViewerState()->GetView3DAttributes()->GetViewUp()[0];
    view3d.viewUp[1] = GetViewerState()->GetView3DAttributes()->GetViewUp()[1];
    view3d.viewUp[2] = GetViewerState()->GetView3DAttributes()->GetViewUp()[2];
    view3d.viewAngle = GetViewerState()->GetView3DAttributes()->GetViewAngle();
    view3d.parallelScale = GetViewerState()->GetView3DAttributes()->GetParallelScale();
    view3d.eyeAngle = GetViewerState()->GetView3DAttributes()->GetEyeAngle();
    view3d.nearPlane = GetViewerState()->GetView3DAttributes()->GetNearPlane();
    view3d.farPlane = GetViewerState()->GetView3DAttributes()->GetFarPlane();
    view3d.imagePan[0] = GetViewerState()->GetView3DAttributes()->GetImagePan()[0];
    view3d.imagePan[1] = GetViewerState()->GetView3DAttributes()->GetImagePan()[1];
    view3d.imageZoom = GetViewerState()->GetView3DAttributes()->GetImageZoom();
    view3d.perspective = GetViewerState()->GetView3DAttributes()->GetPerspective();
    view3d.centerOfRotationSet = GetViewerState()->GetView3DAttributes()->GetCenterOfRotationSet();
    view3d.centerOfRotation[0] = GetViewerState()->GetView3DAttributes()->GetCenterOfRotation()[0];
    view3d.centerOfRotation[1] = GetViewerState()->GetView3DAttributes()->GetCenterOfRotation()[1];
    view3d.centerOfRotation[2] = GetViewerState()->GetView3DAttributes()->GetCenterOfRotation()[2];
    view3d.axis3DScaleFlag = GetViewerState()->GetView3DAttributes()->GetAxis3DScaleFlag();
    view3d.axis3DScales[0] = GetViewerState()->GetView3DAttributes()->GetAxis3DScales()[0];
    view3d.axis3DScales[1] = GetViewerState()->GetView3DAttributes()->GetAxis3DScales()[1];
    view3d.axis3DScales[2] = GetViewerState()->GetView3DAttributes()->GetAxis3DScales()[2];
    view3d.shear[0] = GetViewerState()->GetView3DAttributes()->GetShear()[0];
    view3d.shear[1] = GetViewerState()->GetView3DAttributes()->GetShear()[1];
    view3d.shear[2] = GetViewerState()->GetView3DAttributes()->GetShear()[2];
    view3d.windowValid = GetViewerState()->GetView3DAttributes()->GetWindowValid();

    //
    // Set the 3D view for the active viewer window.
    //
    windows[activeWindow]->SetView3D(view3d);

    //
    // This will maintain our internal state and also make locked windows
    // get this view.
    //
    UpdateViewAtts(activeWindow, false, false, true, false);
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetViewAxisArrayFromClient
//
//  Purpose:
//    Sets the view for the active window using the client view attributes.
//
//  Programmer: Jeremy Meredith
//  Creation:   February  4, 2008
//
//  Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetViewAxisArrayFromClient()
{
    avtViewAxisArray viewAxisArray;

    viewAxisArray.domain[0] = GetViewerState()->GetViewAxisArrayAttributes()->GetDomainCoords()[0];
    viewAxisArray.domain[1] = GetViewerState()->GetViewAxisArrayAttributes()->GetDomainCoords()[1];

    viewAxisArray.range[0] = GetViewerState()->GetViewAxisArrayAttributes()->GetRangeCoords()[0];
    viewAxisArray.range[1] = GetViewerState()->GetViewAxisArrayAttributes()->GetRangeCoords()[1];

    viewAxisArray.viewport[0] = GetViewerState()->GetViewAxisArrayAttributes()->GetViewportCoords()[0];
    viewAxisArray.viewport[1] = GetViewerState()->GetViewAxisArrayAttributes()->GetViewportCoords()[1];
    viewAxisArray.viewport[2] = GetViewerState()->GetViewAxisArrayAttributes()->GetViewportCoords()[2];
    viewAxisArray.viewport[3] = GetViewerState()->GetViewAxisArrayAttributes()->GetViewportCoords()[3];

    //
    // Set the AxisArray view for the active viewer window.
    //
    windows[activeWindow]->SetViewAxisArray(viewAxisArray);

    //
    // This will maintain our internal state and also make locked windows
    // get this view.
    //
    UpdateViewAtts(activeWindow, false, false, false, true);
}

// ****************************************************************************
//  Method: ViewerWindowManager::ClearViewKeyframes
//
//  Purpose:
//    Clears the view keyframes for the active window.
//
//  Programmer: Eric Brugger
//  Creation:   January 6, 2003
//
//  Modifications:
//    Brad Whitlock, Fri Jan 23 15:38:17 PST 2004
//    Moved update code into a method.
//
// ****************************************************************************

void
ViewerWindowManager::ClearViewKeyframes()
{
    windows[activeWindow]->ClearViewKeyframes();
    UpdateViewKeyframeInformation();
}

// ****************************************************************************
//  Method: ViewerWindowManager::DeleteViewKeyframe
//
//  Purpose:
//    Deletes a view keyframe from the active window.
//
//  Arguments:
//    frame     The keyframe to delete.
//
//  Programmer: Eric Brugger
//  Creation:   January 6, 2003
//
//  Modifications:
//    Brad Whitlock, Fri Jan 23 15:38:17 PST 2004
//    Moved update code into a method.
//
// ****************************************************************************

void
ViewerWindowManager::DeleteViewKeyframe(const int frame)
{
    windows[activeWindow]->DeleteViewKeyframe(frame);
    UpdateViewKeyframeInformation();
}

// ****************************************************************************
//  Method: ViewerWindowManager::MoveViewKeyframe
//
//  Purpose:
//    Moves the position of a view keyframe.
//
//  Arguments:
//    oldFrame  The old location of the keyframe.
//    newFrame  The new location of the keyframe.
//
//  Programmer: Eric Brugger
//  Creation:   January 29, 2003
//
//  Modifications:
//    Brad Whitlock, Fri Jan 23 15:38:17 PST 2004
//    Moved update code into a method.
//
// ****************************************************************************

void
ViewerWindowManager::MoveViewKeyframe(int oldFrame, int newFrame)
{
    windows[activeWindow]->MoveViewKeyframe(oldFrame, newFrame);
    UpdateViewKeyframeInformation();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetViewKeyframe
//
//  Purpose:
//    Sets a view keyframe for the active window.
//
//  Programmer: Eric Brugger
//  Creation:   January 6, 2003
//
//  Modifications:
//    Brad Whitlock, Fri Jan 23 15:38:17 PST 2004
//    Moved update code into a method.
//
// ****************************************************************************

void
ViewerWindowManager::SetViewKeyframe()
{
    windows[activeWindow]->SetViewKeyframe();
    UpdateViewKeyframeInformation();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetViewExtentsType
//
//  Purpose:
//    This method specifies which flavor of view extents to use to determine
//    the view.
//
//  Arguments:
//    viewType     The flavor of spatial extents to use when setting the view.
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
// Programmer: Hank Childs
// Creation:   July 15, 2002
//
// Modifications:
//   Brad Whitlock, Mon Sep 16 15:21:09 PST 2002
//   I made it update the WindowInformation. I also made it recenter the
//   view if the window's autocentering flag is on.
//
//   Eric Brugger, Fri Apr 18 12:38:05 PDT 2003
//   I replaced auto center mode with maintain view mode.
//
//   Brad Whitlock, Tue Feb 3 16:03:19 PST 2004
//   I made it use window information.
//
// ****************************************************************************

void
ViewerWindowManager::SetViewExtentsType(avtExtentType viewType,
                                        int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->SetViewExtentsType(viewType);
        if(!windows[index]->GetMaintainViewMode())
            RecenterView(index);

        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SetRenderingAttributes
//
// Purpose:
//   Tells the specified window to use the current rendering attributes.
//
// Arguments:
//   windowIndex : The index of the window to use. If the value is -1  then
//                 the active window is used.
//
// Programmer: Brad Whitlock
// Creation:   Thu Sep 19 13:43:11 PST 2002
//
// Modifications:
//   Kathleen Bonnell, Wed Dec  4 17:38:27 PST 2002
//   Removed antialisingFrames, no lnger needed.
//
//   Brad Whitlock, Tue Jul 8 11:10:10 PDT 2003
//   Added a flag that lets the window update when turning on scalable
//   rendering.
//
//   Brad Whitlock, Wed Aug 27 17:20:19 PST 2003
//   I changed the code so it redraws the window like it's supposed to after
//   setting rendering options like the surface representation.
//
//   Mark C. Miller, Mon Nov  3 15:29:57 PST 2003
//   I made it so only those parts of rendereing attributes that actually
//   were changed are changed on the window
//
//   Jeremy Meredith, Fri Nov 14 12:23:19 PST 2003
//   Added specular properties.
//
//   Brad Whitlock, Tue Feb 3 16:03:47 PST 2004
//   I made it use window information.
//
//   Hank Childs, Mon May 10 08:10:40 PDT 2004
//   Replace references to immediate mode rendering with display list mode.
//
//   Mark C. Miller, Tue May 11 20:21:24 PDT 2004
//   Modified calls to set scalable controls to accomdate scalable activaation
//   mode and scalable auto threshold
//
//   Hank Childs, Sun Oct 24 13:39:57 PDT 2004
//   Added shading properties.
//
//   Brad Whitlock, Mon Sep 18 10:42:40 PDT 2006
//   Added a flag for color texturing.
//
//   Jeremy Meredith, Wed Aug 29 15:23:19 EDT 2007
//   Added depth cueing properties.
//
//   Jeremy Meredith, Fri Apr 30 14:39:07 EDT 2010
//   Added automatic depth cueing mode.
//
//    Dave Pugmire, Tue Aug 24 11:32:12 EDT 2010
//    Add compact domain options.
//
//    Eric Brugger, Fri Oct 28 10:07:28 PDT 2011
//    Add a multi resolution display capability for AMR data.
//
//    Burlen Loring, Thu Aug 13 08:38:52 PDT 2015
//    Added options for depth peeling
//
//    Burlen Loring, Sun Sep  6 08:44:26 PDT 2015
//    Added option to disable ordered composting
//
//    Burlen Loring, Tue Sep 29 14:25:19 PDT 2015
//    Added options for compositer threading.
//
//    Garrett Morrison, Fri May 11 17:57:47 PDT 2018
//    Added options for ospray rendering
//
// ****************************************************************************

void
ViewerWindowManager::SetRenderingAttributes(int windowIndex)
{
    // Note: rendering attributes are percolated via the follwing path:
    // ViewerWindowManager->ViewerWindow->VisWindow->VisWinRendering
    // If new rendering attributes are introduced ALL of the above
    // classes (in multiple places) must be updated.

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool updatesEnabled = windows[index]->UpdatesEnabled();
        windows[index]->DisableUpdates();

        const RenderingAttributes *ratts = GetViewerState()->GetRenderingAttributes();

        if (windows[index]->GetAntialiasing() != ratts->GetAntialiasing())
            windows[index]->SetAntialiasing(ratts->GetAntialiasing());

        if (windows[index]->GetOrderComposite() != ratts->GetOrderComposite())
            windows[index]->SetOrderComposite(ratts->GetOrderComposite());

        if (windows[index]->GetDepthCompositeThreads() !=
            static_cast<size_t>( ratts->GetDepthCompositeThreads()))
            windows[index]->SetDepthCompositeThreads(ratts->GetDepthCompositeThreads());

        if (windows[index]->GetAlphaCompositeThreads() !=
            static_cast<size_t>(ratts->GetAlphaCompositeThreads()))
            windows[index]->SetAlphaCompositeThreads(ratts->GetAlphaCompositeThreads());

        if (windows[index]->GetDepthCompositeBlocking() !=
            static_cast<size_t>(ratts->GetDepthCompositeBlocking()))
            windows[index]->SetDepthCompositeBlocking(ratts->GetDepthCompositeBlocking());

        if (windows[index]->GetAlphaCompositeBlocking() !=
            static_cast<size_t>( ratts->GetAlphaCompositeBlocking()))
            windows[index]->SetAlphaCompositeBlocking(ratts->GetAlphaCompositeBlocking());

        if (windows[index]->GetDepthPeeling() != ratts->GetDepthPeeling())
            windows[index]->SetDepthPeeling(ratts->GetDepthPeeling());

        if (windows[index]->GetOcclusionRatio() != ratts->GetOcclusionRatio())
            windows[index]->SetOcclusionRatio(ratts->GetOcclusionRatio());

        if (windows[index]->GetNumberOfPeels() != ratts->GetNumberOfPeels())
            windows[index]->SetNumberOfPeels(ratts->GetNumberOfPeels());

        if (windows[index]->GetMultiresolutionMode() != ratts->GetMultiresolutionMode())
            windows[index]->SetMultiresolutionMode(ratts->GetMultiresolutionMode());

        if (windows[index]->GetMultiresolutionCellSize() !=
            ratts->GetMultiresolutionCellSize())
            windows[index]->SetMultiresolutionCellSize(
            ratts->GetMultiresolutionCellSize());

        if (windows[index]->GetSurfaceRepresentation() !=
            (int) ratts->GetGeometryRepresentation())
            windows[index]->SetSurfaceRepresentation((int)
            ratts->GetGeometryRepresentation());

        if ((windows[index]->GetStereo() != ratts->GetStereoRendering()) ||
            (windows[index]->GetStereoType() != (int) ratts->GetStereoType()))
            windows[index]->SetStereoRendering(ratts->GetStereoRendering(),
                (int)ratts->GetStereoType());

        if (windows[index]->GetNotifyForEachRender() !=
            ratts->GetNotifyForEachRender())
            windows[index]->SetNotifyForEachRender(ratts->GetNotifyForEachRender());

        if (windows[index]->GetScalableAutoThreshold() !=
            ratts->GetScalableAutoThreshold())
            windows[index]->SetScalableAutoThreshold(ratts->GetScalableAutoThreshold());

        if (windows[index]->GetScalableActivationMode() !=
            ratts->GetScalableActivationMode())
            windows[index]->SetScalableActivationMode(ratts->GetScalableActivationMode());

        if (windows[index]->GetCompactDomainsActivationMode() !=
            ratts->GetCompactDomainsActivationMode())
            windows[index]->SetCompactDomainsActivationMode(ratts->GetCompactDomainsActivationMode());
        if (windows[index]->GetCompactDomainsAutoThreshold() !=
            ratts->GetCompactDomainsAutoThreshold())
            windows[index]->SetCompactDomainsAutoThreshold(ratts->GetCompactDomainsAutoThreshold());

        if (windows[index]->GetCompressionActivationMode() !=
            ratts->GetCompressionActivationMode())
            windows[index]->SetCompressionActivationMode(ratts->GetCompressionActivationMode());

        if (windows[index]->GetSpecularFlag()  != ratts->GetSpecularFlag() ||
            windows[index]->GetSpecularCoeff() != ratts->GetSpecularCoeff() ||
            windows[index]->GetSpecularPower() != ratts->GetSpecularPower() ||
            windows[index]->GetSpecularColor() != ratts->GetSpecularColor())
        {
            windows[index]->SetSpecularProperties(ratts->GetSpecularFlag(),
                                                 ratts->GetSpecularCoeff(),
                                                 ratts->GetSpecularPower(),
                                                 ratts->GetSpecularColor());
        }

        if (windows[index]->GetDoShading() != ratts->GetDoShadowing() ||
            windows[index]->GetShadingStrength() !=
                                              ratts->GetShadowStrength())
        {
            windows[index]->SetShadingProperties(ratts->GetDoShadowing(),
                                                 ratts->GetShadowStrength());
        }

        if (windows[index]->GetDoDepthCueing() != ratts->GetDoDepthCueing() ||
            windows[index]->GetDepthCueingAutomatic() != ratts->GetDepthCueingAutomatic() ||
            windows[index]->GetStartCuePoint()[0] != ratts->GetStartCuePoint()[0] ||
            windows[index]->GetStartCuePoint()[1] != ratts->GetStartCuePoint()[1] ||
            windows[index]->GetStartCuePoint()[2] != ratts->GetStartCuePoint()[2] ||
            windows[index]->GetEndCuePoint()[0] != ratts->GetEndCuePoint()[0] ||
            windows[index]->GetEndCuePoint()[1] != ratts->GetEndCuePoint()[1] ||
            windows[index]->GetEndCuePoint()[2] != ratts->GetEndCuePoint()[2])
        {
            windows[index]->SetDepthCueingProperties(
                                               ratts->GetDoDepthCueing(),
                                               ratts->GetDepthCueingAutomatic(),
                                               ratts->GetStartCuePoint(),
                                               ratts->GetEndCuePoint());
        }

        if (windows[index]->GetColorTexturingFlag() !=
            ratts->GetColorTexturingFlag())
        {
            windows[index]->SetColorTexturingFlag(
                ratts->GetColorTexturingFlag());
        }

#if defined(HAVE_OSPRAY)
        if (windows[index]->GetOsprayRendering() != ratts->GetOsprayRendering())
            windows[index]->SetOsprayRendering(ratts->GetOsprayRendering());
        if (windows[index]->GetOspraySPP() != ratts->GetOspraySPP())
            windows[index]->SetOspraySPP(ratts->GetOspraySPP());
        if (windows[index]->GetOsprayAO() != ratts->GetOsprayAO())
            windows[index]->SetOsprayAO(ratts->GetOsprayAO());
        if (windows[index]->GetOsprayShadows() != ratts->GetOsprayShadows())
            windows[index]->SetOsprayShadows(ratts->GetOsprayShadows());
#endif

        // If the updatesEnabled flag was true before we temporarily disabled
        // updates, turn updates back on and force the window to redraw so the
        // rendering options such as the surface representation and antialiasing
        // are noticed in the vis window right away.
        if (updatesEnabled)
        {
            windows[index]->EnableUpdates();
            windows[index]->RedrawWindow();
        }

        UpdateRenderingAtts(index);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }

}

// ****************************************************************************
//  Method: ViewerWindowManager::ToggleSpinMode
//
//  Purpose:
//    This method toggles the spin mode for the specified window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
// Programmer: Hank Childs
// Creation:   May 29, 2002
//
// Modifications:
//   Brad Whitlock, Mon Sep 16 15:21:09 PST 2002
//   I made it update the WindowInformation.
//
// ****************************************************************************

void
ViewerWindowManager::ToggleSpinMode(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool spinMode = windows[index]->GetSpinMode();
        windows[index]->SetSpinMode(!spinMode);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::ToggleCameraViewMode
//
//  Purpose:
//    This method toggles the camera view mode for the specified window.
//
//  Programmer: Eric Brugger
//  Creation:   January 6, 2003
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Modifications:
//    Jeremy Meredith, Tue Feb  4 17:43:19 PST 2003
//    Added code to update the window information if the camera view mode
//    changes.  (I also added the mode info to the WindowInformation, which
//    is what made this necessary.)
//
//    Brad Whitlock, Tue Feb 3 16:04:12 PST 2004
//    I made it use window information.
//
// ****************************************************************************

void
ViewerWindowManager::ToggleCameraViewMode(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool cameraViewMode = windows[index]->GetCameraViewMode();
        windows[index]->SetCameraViewMode(!cameraViewMode);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::AskForCorrelationPermission
//
// Purpose:
//   Asks the user if a correlation should be created.
//
// Arguments:
//   msg   : The message to display.
//   title : The title of the dialog window.
//   dbs   : The databases that will be correlated.
//
// Returns:    True if the correlation should be created; false otherwise.
//
// Programmer: Brad Whitlock
// Creation:   Mon Mar 29 09:53:14 PDT 2004
//
// Modifications:
//   Brad Whitlock, Tue Apr 14 11:29:21 PDT 2009
//   Use ViewerProperties.
//
// ****************************************************************************

bool
ViewerWindowManager::AskForCorrelationPermission(const ViewerText &msg,
    const ViewerText &title, const stringVector &dbs) const
{
    bool permission;

    if(GetViewerProperties()->GetNowin())
        permission = true;
    else
    {
        // Pop up a dialog to ask the user whether or not to correlate
        // the specified databases.
        std::string text;
        for(size_t i = 0; i < dbs.size(); ++i)
            text += (dbs[i] + "\n");
        ViewerText msg2(ViewerText("\n\n%1").arg(text));
        permission = GetViewerMessaging()->InformationBox(title, msg + msg2);
    }

    return permission;
}

// ****************************************************************************
// Method: ViewerWindowManager::CreateMultiWindowCorrelationHelper
//
// Purpose:
//   Creates a new multiwindow database correlation or alters an existing
//   database correlation so it supports all of the specified databases.
//
// Arguments:
//   dbs : The list of databases for which we want a database correlation.
//
// Returns:    A pointer to the database correlation that we'll use or 0
//             if there is no correlation to use.
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Mon Mar 29 09:54:27 PDT 2004
//
// Modifications:
//   Brad Whitlock, Fri Mar 18 10:37:21 PDT 2005
//   I made the correlation be returned in the event that the user chose
//   to create a new multiwindow database correlation or alter a database
//   correlation.
//
// ****************************************************************************

DatabaseCorrelation *
ViewerWindowManager::CreateMultiWindowCorrelationHelper(const stringVector &dbs)
{
    //
    // Get the most suitable correlation for the list of dbs.
    //
    bool createNewCorrelation = true;
    DatabaseCorrelationList *cL = GetViewerState()->GetDatabaseCorrelationList();
    DatabaseCorrelation *correlation = GetViewerStateManager()->
        GetDatabaseCorrelationMethods()->GetMostSuitableCorrelation(dbs);
    ViewerText msg;

    if(correlation)
    {
        //
        // If the number of databases in the correlation is less than the
        // number of databases in the dbs list then ask the user if the
        // correlation should be modfified.
        //
        if((size_t)correlation->GetNumDatabases() < dbs.size())
        {
            msg = TR("Would you like to modify the %1 correlation so "
                     "it correlates the following databases?").
                  arg(correlation->GetName().c_str());
            if(AskForCorrelationPermission(msg, TR("Alter correlation?"), dbs))
            {
                createNewCorrelation = false;

                //
                // Alter the correlation
                //
                AlterDatabaseCorrelation(correlation->GetName(), dbs,
                    correlation->GetMethod());
            }
            else
                correlation = 0;
        }
        else
        {
            // We found a correlation that matched perfectly. Do nothing.
            createNewCorrelation = false;
        }
    }

    if(createNewCorrelation)
    {
        ViewerText prompt(TR(
            "Would you like to correlate the following databases "
            "to ensure that changing the time will apply to all "
            "windows that are locked in time?"));
        if(AskForCorrelationPermission(prompt, TR("Create correlation?"), dbs))
        {
            //
            // Create a new database correlation since there was no suitable
            // database correlation.
            //
            std::string newName(GetViewerStateManager()->
                GetDatabaseCorrelationMethods()->CreateNewCorrelationName());
            correlation = GetViewerStateManager()->
                GetDatabaseCorrelationMethods()->CreateDatabaseCorrelation(
                    newName, dbs, cL->GetDefaultCorrelationMethod());
            if(correlation)
            {
                // Add the new correlation to the correlation list.
                cL->AddCorrelations(*correlation);
                cL->Notify();
                delete correlation;
                correlation = cL->FindCorrelation(newName);

                debug3 << "Created a new correlation called: "
                       << newName.c_str() << endl << *correlation << endl;

                // Tell the user about the new correlation.
                msg = TR("VisIt created a new database correlation "
                         "called %1.").arg(correlation->GetName());
                GetViewerMessaging()->Message(msg);
            }

            cL->Notify();
        }
        else
        {
            //
            // The user opted to not create a multi-window correlation.
            // Issue a warning message.
            //
            GetViewerMessaging()->Warning(
                TR("Since you opted not to create a database correlation, "
                   "changing time sliders in one locked window might not "
                   "affect other locked windows."));
            correlation = 0;
        }
    }

    return correlation;
}

// ****************************************************************************
// Method: ViewerWindowManager::CreateMultiWindowCorrelation
//
// Purpose:
//   Creates a database correlation that involves multiple windows.
//
// Arguments:
//   windowIds : The window ids of the windows that we want to involve in
//               creating the database correlation.
//
// Programmer: Brad Whitlock
// Creation:   Mon Mar 29 11:20:16 PDT 2004
//
// Modifications:
//   Brad Whitlock, Wed Mar 16 17:40:02 PST 2005
//   I made it return the database correlation to use for multiple windows.
//
//   Brad Whitlock, Tue Mar 27 11:15:54 PDT 2007
//   Pass false for new argument in GetDatabasesForWindows so only the MT
//   databases are considered -- this was the previous behavior.
//
// ****************************************************************************

DatabaseCorrelation *
ViewerWindowManager::CreateMultiWindowCorrelation(const intVector &windowIds)
{
    //
    // Get the list of databases for all of the time-locked windows.
    //
    stringVector dbs;
    GetDatabasesForWindows(windowIds, dbs, false);
    DatabaseCorrelation *correlation = 0;

    if(dbs.size() == 1)
    {
        // All of the locked windows used a single time slider so nothing
        // needs to be done. Alternatively, none of the locked windows
        // even had an active time slider so nothing needs to be done.
        correlation = GetViewerState()->GetDatabaseCorrelationList()->
            FindCorrelation(dbs[0]);
    }
    else if(dbs.size() > 1)
    {
        //
        // Get a correlation that has all of the databases in it. If such a
        // correlation does not exist then prompt the user to create one.
        // Either modify an existing correlation or create a brand new one.
        //
        correlation = CreateMultiWindowCorrelationHelper(dbs);

        //
        // If we had to create or edit a correlation, set the active time
        // slider for each time-locked window to be that correlation if the
        // time-locked window has an active time slider. We don't want to
        // change windows that don't have an active time slider. Use the
        // old time slider in each window to set the state for the new
        // time slider.
        //
        for(size_t i = 0; i < windowIds.size(); ++i)
        {
            ViewerPlotList *pl = windows[windowIds[i]]->GetPlotList();
            if(correlation != 0 && pl->HasActiveTimeSlider())
            {
                std::string ts(pl->GetActiveTimeSlider());

                //
                // If the window's current time slider is the same as
                // the new correlation, then don't change the time slider.
                //
                if(ts != correlation->GetName())
                {
                    //
                    // We found a correlation for the time slider so it's
                    // a valid time slider. Get the time state for the
                    // time slider and feed it through the multiwindow
                    // correlation to get the new time slider's time
                    // state.
                    int state = 0, nStates = 0;
                    pl->GetTimeSliderStates(ts, state, nStates);
                    int cts = correlation->GetCorrelatedTimeState(ts, state);

                    //
                    // Create a new time slider using the name of the
                    // new multiwindow correlation.
                    //
                    if(!pl->TimeSliderExists(correlation->GetName()))
                    {
                        debug2 << "Creating " << correlation->GetName().c_str()
                               << " time slider in window " << i+1
                               << " and making it the active time slider.\n";
                        pl->CreateTimeSlider(correlation->GetName(), cts);
                    }

                    //
                    // Make the new time slider be the active time slider.
                    //
                    pl->SetActiveTimeSlider(correlation->GetName());
                }
            }
        }
    }

    return correlation;
}

// ****************************************************************************
// Method: ViewerWindowManager::ToggleLockTime
//
// Purpose:
//   This method toggles the lock time mode for the specified window.
//
// Arguments:
//    windowIndex : This is a zero-origin integer that specifies the index
//                  of the window we want to change. If the value is -1, use
//                  use the active window.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 11:59:05 PDT 2002
//
// Modifications:
//   Eric Brugger, Mon Dec  8 08:24:16 PST 2003
//   I corrected the test to make sure that the number of frames matched
//   between the windows being locked.  I added code to turn on view
//   limit merging.
//
//   Brad Whitlock, Tue Mar 16 09:06:32 PDT 2004
//   I completely rewrote the method so it supports database correlations
//   across windows when locking the windows in time.
//
//   Brad Whitlock, Wed Mar 16 17:34:38 PST 2005
//   Made it use GetTimeLockedWindowIndices.
//
//   Brad Whitlock, Wed Dec 10 16:21:10 PST 2008
//   Use AnimationAttributes.
//
// ****************************************************************************

void
ViewerWindowManager::ToggleLockTime(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        int flags = WINDOWINFO_WINDOWFLAGS;
        bool lockTime = windows[index]->GetTimeLock();
        windows[index]->SetTimeLock(!lockTime);

        if (windows[index]->GetTimeLock())
        {
            //
            // Get the list of window id's that are time-locked windows.
            //
            intVector windowIds;
            GetTimeLockedWindowIndices(windowIds);

            //
            // Create a correlation for all of the time-locked windows.
            //
            CreateMultiWindowCorrelation(windowIds);

            //
            // We have just locked time for this window. Find another
            // window that has locked time and copy its time.
            //
            int winner = -1;
            for (size_t i = 0; i < windowIds.size(); ++i)
            {
                if (windowIds[i] != index)
                {
                    winner = windowIds[i];
                    break;
                }
            }

            //
            // If we found another window from which to copy time and
            // animation settings, copy the values now. It should be okay
            // to do this since we'll have created a correlation to use for
            // all of the time-locked windows.
            //
            if (winner != -1)
            {
                //
                // Get the time slider state from the time slider that we're
                // copying from. The call to GetMultiWindowCorrelation
                // should have changed the active time slider if it was
                // required. If the user did not allow the correlation to
                // be created and the time slider to be changed, they will
                // be different at this point to don't bother setting
                // the time for the window that we just locked.
                //
                ViewerPlotList *fromPL = windows[winner]->GetPlotList();

                if(fromPL->GetActiveTimeSlider() ==
                   windows[index]->GetPlotList()->GetActiveTimeSlider())
                {
                    int tsState, nStates;
                    fromPL->GetTimeSliderStates(fromPL->GetActiveTimeSlider(),
                        tsState, nStates);

                    // Set the time slider state in the window that we
                    // just locked.
                    windows[index]->GetPlotList()->SetTimeSliderState(tsState);
                }

                windows[index]->SetMergeViewLimits(true);
                // Copy the animation atts.
                windows[index]->GetPlotList()->SetAnimationAttributes(
                    fromPL->GetAnimationAttributes());
            }

            // We likely changed time sliders so update them.
            flags |= WINDOWINFO_TIMESLIDERS;
        }

        // Send information back to the client.
        UpdateWindowInformation(flags, index);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ToggleLockTools
//
// Purpose:
//   This method toggles the lock tools mode for the specified window.
//
// Arguments:
//    windowIndex : This is a zero-origin integer that specifies the index
//                  of the window we want to change. If the value is -1, use
//                  use the active window.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 11:59:05 PDT 2002
//
// Modifications:
//   Brad Whitlock, Tue Feb 3 16:05:39 PST 2004
//   Changed how UpdateWindowInformation is called.
//
// ****************************************************************************

void
ViewerWindowManager::ToggleLockTools(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool lockTools = windows[index]->GetToolLock();
        windows[index]->SetToolLock(!lockTools);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::ToggleLockViewMode
//
//  Purpose:
//    This method toggles whether or not the view is locked for the specified
//    window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
// Programmer: Hank Childs
// Creation:   Mon Mar 25 13:56:33 PST 2002
//
// Modifications:
//   Brad Whitlock, Mon Sep 16 15:21:09 PST 2002
//   I made it update the WindowInformation and the view atts if the view
//   changes as a result of locking views.
//
// ****************************************************************************

void
ViewerWindowManager::ToggleLockViewMode(int windowIndex)
{
    if (windowIndex < -1 || windowIndex >= maxWindows)
    {
        return;
    }

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if (windows[index] != 0)
    {
        bool vil = windows[index]->GetViewIsLocked();
        windows[index]->SetViewIsLocked(!vil);
        if (windows[index]->GetViewIsLocked())
        {
            //
            // We have just locked this view.  Find another window that has
            // a locked view and copy its view.
            //
            int winner = -1;
            for (int i = 0; i < maxWindows; i++)
            {
                if (windows[i] != NULL && i != index)
                {
                    if (windows[i]->GetViewIsLocked())
                    {
                        winner = i;
                        break;
                    }
                }
            }
            if (winner != -1)
            {
                const avtView3D &view3d = windows[winner]->GetView3D();
                const avtView2D &view2d = windows[winner]->GetView2D();
                windows[index]->SetView3D(view3d);
                windows[index]->SetView2D(view2d);

                // Update the view attributes.
                if(index == activeWindow)
                    UpdateViewAtts();
            }
        }

        // Update the window information.
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::TurnOffAllLocks
//
// Purpose:
//   Turns off all locks in all windows.
//
// Programmer: Brad Whitlock
// Creation:   Wed Jan 23 10:43:36 PST 2008
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::TurnOffAllLocks()
{
    for(int index = 0; index < maxWindows; ++index)
    {
        if(windows[index] != 0)
        {
            windows[index]->SetViewIsLocked(false);
            windows[index]->SetToolLock(false);
            windows[index]->SetTimeLock(false);
        }
    }

    // Update the window information.
    UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, activeWindow);
}

// ****************************************************************************
//  Method: ViewerWindowManager::TogglePerspective
//
//  Purpose:
//    This method toggles the perspective mode for the specified window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Hank Childs
//  Creation:   November 10, 2000
//
//  Modifications:
//    Brad Whitlock, Fri Jul 27 09:45:47 PDT 2001
//    Added code to send the new view to the client.
//
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
//    Brad Whitlock, Tue Sep 17 11:41:33 PDT 2002
//    I made the window information update.
//
//    Brad Whitlock, Tue Nov 19 14:38:58 PST 2002
//    Changed it so only the 3d view updates.
//
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I changed the call to UpdateViewAtts.
//
// ****************************************************************************

void
ViewerWindowManager::TogglePerspective(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool perspective = windows[index]->GetPerspectiveProjection();
        windows[index]->SetPerspectiveProjection(!perspective);

        //
        // Send the new view info to the client.
        //
        UpdateViewAtts(index, false, false, true);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}


// ****************************************************************************
//  Method: ViewerWindowManager::ToggleFullFrameMode
//
//  Purpose:
//    This method toggles the full frame mode for the specified window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Kathleen Bonnell
//  Creation:   May 13, 2003
//
//  Modifications:
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I changed the call to UpdateViewAtts.
//
//    Brad Whitlock, Tue Feb 3 16:06:34 PST 2004
//    I changed the call to UpdateWindowInformation.
//
//    Dave Bremer, Mon Mar 26 18:36:04 PDT 2007
//    When this gets full frame mode gets toggled on or off, disable auto full
//    frame mode in both the viewer and the gui.
// ****************************************************************************

void
ViewerWindowManager::ToggleFullFrameMode(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool mode = !(windows[index]->GetFullFrameMode());
        windows[index]->SetFullFrameMode(mode);
        windows[index]->SetFullFrameActivationMode(mode?View2DAttributes::On
                                                       :View2DAttributes::Off);
        //
        // Send the new view info to the client.
        //
        UpdateViewAtts(index, false, true, false);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::ToggleMaintainViewMode
//
//  Purpose:
//    This method toggles the maintain view mode for the specified window.
//
//  Arguments:
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 the active window.
//
//  Programmer: Eric Brugger
//  Creation:   April 18, 2003
//
//    Jeremy Meredith, Wed Feb  3 15:35:08 EST 2010
//    Removed maintain data; moved maintain view from Global settings
//    (Main window) to per-window Window Information (View window).
//
// ****************************************************************************

void
ViewerWindowManager::ToggleMaintainViewMode(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        bool maintainView = windows[index]->GetMaintainViewMode();
        windows[index]->SetMaintainViewMode(!maintainView);
        UpdateWindowInformation(WINDOWINFO_WINDOWFLAGS, index);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::UndoView
//
// Purpose:
//   This is a Qt slot function that is called when the popup menu's undo
//   view button is clicked.
//
// Arguments:
//   windowIndex : The index of the window that called this method.
//
// Programmer: Brad Whitlock
// Creation:   Mon Jan 28 15:55:50 PST 2002
//
// Modifications:
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I added undoing of curve views.
//
//    Brad Whitlock, Tue Mar 7 17:32:26 PST 2006
//    I moved most of the code to ViewerWindow.
//
// ****************************************************************************

void
ViewerWindowManager::UndoView(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->UndoView();

        //
        // Send the view to the clients but do not stack it.
        //
        viewStacking = false;
        UpdateViewAtts(index);
        viewStacking = true;
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::RedoView
//
// Purpose:
//   Re-applies a view that has been previously undone.
//
// Arguments:
//   windowIndex : The index of the window that called this method.
//
// Programmer: Brad Whitlock
// Creation:   Tue Mar 7 17:31:44 PST 2006
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::RedoView(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->RedoView();

        //
        // Send the view to the clients but do not stack it.
        //
        viewStacking = false;
        UpdateViewAtts(index);
        viewStacking = true;
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateColorTable
//
//  Purpose:
//    Loops through all windows and tells each one to update for the color
//    table ctName.
//
//  Arguments:
//    ctName    The color table that, if plots use it, they will be updated.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Jun 14 16:24:18 PST 2001
//
// ****************************************************************************

void
ViewerWindowManager::UpdateColorTable(const std::string &ctName)
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
        {
            windows[i]->UpdateColorTable(ctName);
        }
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetWindowLayout
//
//  Purpose:
//    Set the window layout.
//
//  Arguments:
//    windowLayout  The window layout to use (1, 2, 4, 6, 8, or 9).
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
//  Modifications:
//    Brad Whitlock, Wed Nov 1 16:21:45 PST 2000
//    I changed the code so it subtracts border widths when positioning
//    a window that already exists.
//
//    Jeremy Meredith, Fri Jul 20 11:21:45 PDT 2001
//    Added code to (de)iconify windows according the layout.
//
//    Hank Childs, Fri Oct 18 08:22:45 PDT 2002
//    Since CreateVisWindow got split into two routines, we had to call the
//    second one as well.
//
//    Eric Brugger, Fri Feb 13 14:14:41 PST 2004
//    Modified the routine to use both the width and height for the window
//    size, instead of using with width for both.
//
//    Brad Whitlock, Wed Apr 27 16:48:04 PST 2005
//    Changed the code so window layout 1 is handled specially and we get the
//    active window showing instead of window 1.
//
//    Brad Whitlock, Wed Apr 30 09:58:46 PDT 2008
//    Added tr().
//
//    Mark C. Miller, Fri Mar 17 15:14:30 PDT 2023
//    Add APPLE-specific logic to address blank viewer windows (#18090)
// ****************************************************************************

void
ViewerWindowManager::SetWindowLayout(const int windowLayout)
{
    // Stop gap to avert blank viewer windows (#18090). There is associated
    // logic in rpc/ViewerMethods::DrawPlots which will trigger this logic upon
    // **FIRST** draw. The goal is to cause all windows to repaint and not be
    // blank.
#if defined(__APPLE__)
    int const DiddleSizeToFixBlankStartup = -5;
    if (windowLayout == DiddleSizeToFixBlankStartup)
    {
        for (int iWindow = 0; iWindow < maxWindows; iWindow++)
        {
            if (windows[iWindow] == 0)
                continue;

            int origWidth, origHeight;
            windows[iWindow]->GetWindowSize(origWidth, origHeight);
            windows[iWindow]->SetSize(origWidth-1, origHeight);
            windows[iWindow]->SetSize(origWidth, origHeight);
        }
        return;
    }
#endif

    //
    // Determine if it is a valid layout and use the index in the valid
    // layout array to set the layoutIndex.
    //
    int       iLayout;

    for (iLayout = 0; iLayout < maxLayouts; iLayout++)
    {
        if (validLayouts[iLayout] == windowLayout) break;
    }

    if (iLayout == maxLayouts)
    {
        ViewerText msg(TR("Window layout %1 is an unsupported layout.").
                          arg(windowLayout));
        GetViewerMessaging()->Error(msg);
        return;
    }

    layout = windowLayout;
    layoutIndex = iLayout;

    if(layoutIndex == 0)
    {
        for (int iWindow = 0; iWindow < maxWindows; iWindow++)
        {
            if(windows[iWindow] != 0 && windows[iWindow] != GetActiveWindow())
                windows[iWindow]->Iconify();
        }

        int x      = windowLimits[layoutIndex][0].x;
        int y      = windowLimits[layoutIndex][0].y;
        int width  = windowLimits[layoutIndex][0].width;
        int height = windowLimits[layoutIndex][0].height;
        GetActiveWindow()->DeIconify();
        GetActiveWindow()->SetSize(width, height);
        GetActiveWindow()->SetLocation(x, y);
    }
    else
    {
        //
        // Create at least "layout" windows layed out in the appropriate grid.
        // If there are already more windows than the layout calls for then
        // put the first "layout" window in the grid and layout the remaining
        // windows down a diagonal through the grid.
        //
        int nWindowsShort = layout - nWindows;
        int nWindowsProcessed = 0;

        for (int iWindow = 0; iWindow < maxWindows; iWindow++)
        {
            int       x, y;
            int       width, height;

            //
            // If the window exists, position it properly.
            //
            if (windows[iWindow] != 0)
            {
                if (nWindowsProcessed < layout)
                {
                    windows[iWindow]->DeIconify();
                    x      = windowLimits[layoutIndex][nWindowsProcessed].x;
                    y      = windowLimits[layoutIndex][nWindowsProcessed].y;
                    width  = windowLimits[layoutIndex][nWindowsProcessed].width;
                    height = windowLimits[layoutIndex][nWindowsProcessed].height;
                    windows[iWindow]->SetSize(width, height);
                    windows[iWindow]->SetLocation(x, y);
                }
                else
                {
                    windows[iWindow]->Iconify();
                }
                nWindowsProcessed++;
            }
            //
            // If the window doesn't exist and we still don't have enough,
            // then create one in the correct location.
            //
            else if (nWindowsShort > 0)
            {
                x      = windowLimits[layoutIndex][iWindow].x;
                y      = windowLimits[layoutIndex][iWindow].y;
                width  = windowLimits[layoutIndex][iWindow].width;
                height = windowLimits[layoutIndex][iWindow].height;

                CreateVisWindow(iWindow, width, height, x, y);
                SetWindowAttributes(iWindow, false);

                nWindowsProcessed++;
                nWindowsShort--;
            }
        }
    }

    //
    // Update the client global attributes.
    //
    UpdateGlobalAtts();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetActiveWindow
//
//  Purpose:
//    Set the active window.
//
//  Arguments:
//    windowId      The 1 origin window identifier.
//    raiseWindow   Flag specifying whether window should be raised
//
//  Programmer: Eric Brugger
//  Creation:   September 13, 2000
//
//  Modifications:
//    Brad Whitlock, Fri Jul 27 09:49:45 PDT 2001
//    Added code to send the view for the active window to the client.
//
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
//    Brad Whitlock, Thu Aug 30 09:45:21 PDT 2001
//    Added code to update the client annotation attributes.
//
//    Brad Whitlock, Fri Sep 14 15:25:06 PST 2001
//    Added code to update the light list.
//
//    Eric Brugger, Fri Nov  2 12:39:55 PST 2001
//    I added code to copy the attributes from the currently active window
//    to the newly active window if it is being referenced for the first
//    time.
//
//    Eric Brugger, Wed Nov 21 12:12:45 PST 2001
//    I added animation attributes.
//
//    Kathleen Bonnell, Tue Nov 27 16:03:00 PST 2001
//    Added pick attributes.
//
//    Brad Whitlock, Mon Feb 4 14:58:05 PST 2002
//    Moved some code out into UpdateAllAtts.
//
//    Sean Ahern, Mon May 20 14:15:16 PDT 2002
//    Made windows raise when they are activated.
//
//    Kathleen Bonnell, Fri Nov 15 09:07:36 PST 2002
//    Removed call to CopyPickAttributes.
//
//    Brad Whitlock, Wed Feb 12 11:05:38 PDT 2003
//    I added CopyAnimation.
//
//    Eric Brugger, Thu Mar 13 10:50:11 PST 2003
//    I implemented CloneWindowOnFirstRef mode, which clones the current
//    window to the newly activated window when it is first referenced.
//
//    Brad Whitlock, Tue Dec 30 14:41:38 PST 2003
//    Added code to make sure the new active window is not iconified.
//
//    Brad Whitlock, Tue Jan 27 17:37:31 PST 2004
//    I changed the copy codea little bit.
//
//    Brad Whitlock, Wed Mar 16 15:18:43 PST 2005
//    I made the active window's database be copied to the new window's
//    plot list if the new window has not been referenced and its database
//    has not been set. This prevents problems where the new window does not
//    have a database and the right time sliders.
//
//    Brad Whitlock, Mon Mar 26 16:43:07 PST 2007
//    Copy the annotation objects too.
//
//    Brad Whitlock, Wed Apr 30 09:58:30 PDT 2008
//    Added tr().
//
//    Brad Whitlock, Fri Aug 13 16:38:06 PDT 2010
//    Fix legend names.
//
//    Gunther H. Weber, Mon Jul 11 11:57:06 PDT 2011
//    Added flag indicating whether window should be raised.
//
//    Gunther H. Weber, Mon Jul 18 16:27:41 PDT 2011
//    Activate window before raising it (otherwise MacOS will only
//    raise the window briefly.
//
// ****************************************************************************

void
ViewerWindowManager::SetActiveWindow(const int windowId, const bool raiseWindow)
{
    //
    // Check the window id.
    //
    int winIndex = windowId - 1;
    if (windowId <= 0 || windowId > maxWindows || windows[winIndex] == 0)
    {
        GetViewerMessaging()->Error(TR("The specified window doesn't exist."));
        return;
    }

    //
    // Copy the window attributes from the current window to the new
    // window if the new window has been referenced for the first time.
    //
    ViewerWindow *dest = windows[winIndex];
    ViewerWindow *src = windows[activeWindow];
    if (GetViewerState()->GetGlobalAttributes()->GetCloneWindowOnFirstRef())
    {
        if(referenced[winIndex])
        {
            // The window has been referenced before but it does not have
            // a database. In this case, since we are probably going back to
            // window 1 after doing stuff in window N, copy the plot list.
            if(dest->GetPlotList()->GetHostDatabaseName() == "")
                dest->GetPlotList()->CopyFrom(src->GetPlotList(), false);
        }
        else
        {
            //
            // Copy the global attributes, the annotation attributes, the
            // light source attributes, the view attributes and the animation
            // attributes.
            //
            dest->CopyGeneralAttributes(src);
            dest->CopyAnnotationAttributes(src);
            dest->CopyLightList(src);
            dest->CopyViewAttributes(src);
            StringStringMap nameMap = dest->GetPlotList()->CopyFrom(src->GetPlotList(), true);
            dest->CopyAnnotationObjectList(src, nameMap, true);
        }
    }
    else
    {
        // We're not cloning everything when going to the new window but
        // if the window has not been referenced before then we still want
        // to make sure that the new window has a database and the right
        // time sliders.
        if(!referenced[winIndex] &&
           dest->GetPlotList()->GetHostDatabaseName() == "")
        {
            debug2 << "Window " << windowId << ", a window that is being "
                "referenced for the first time, is having its database "
                "set to: " << src->GetPlotList()->GetHostDatabaseName().c_str()
                   << ".\n";
            dest->GetPlotList()->CopyFrom(src->GetPlotList(), false);
        }
    }

    referenced[winIndex] = true;

    //
    // Make the specified window active.
    //
    activeWindow = winIndex;

    // Deiconify the activated window.
    windows[activeWindow]->DeIconify();


    if (raiseWindow)
    {
        // Activate window in windowing system
        windows[activeWindow]->ActivateWindow();

        // Raise the activated window.
        windows[activeWindow]->Raise();
    }

    //
    // Update all of the client window attributes.
    //
    UpdateAllAtts();
}

// ****************************************************************************
//  Method: ViewerWindowManager::GetActiveWindow
//
//  Purpose:
//    Return the a pointer to the currently active window.
//
//  Returns:    A pointer to the currently active window.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Nov 7 12:09:31 PDT 2000'
//
//  Modifications:
//    Kathleen Bonnell, Fri Apr 19 09:07:13 PDT 2002
//    Changed error message to report correct method name
//
//    Brad Whitlock, Wed Apr 30 09:59:11 PDT 2008
//    Added tr().
//
// ****************************************************************************

ViewerWindow *
ViewerWindowManager::GetActiveWindow() const
{
    //
    // If there are no windows it is an error.
    //
    if (nWindows == 0)
    {
        GetViewerMessaging()->Error(TR("There is no active window."));
    }

    return windows[activeWindow];
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateGlobalAtts
//
//  Purpose:
//    Update the client global attributes.
//
//  Programmer: Eric Brugger
//  Creation:   September 21, 2000
//
//  Modifications:
//    Brad Whitlock, Tue Nov 7 09:56:23 PDT 2000
//    Changed to reflect that animations are now part of ViewerWindow.
//
//    Eric Brugger, Mon Oct 29 16:44:21 PST 2001
//    Added code to set the animation mode.
//
//    Brad Whitlock, Mon Sep 16 14:58:55 PST 2002
//    Added code to set the window layout.
//
//    Eric Brugger, Fri Nov 15 16:32:25 PST 2002
//    Added support for keyframing.
//
//    Brad Whitlock, Mon Dec 30 14:14:31 PST 2002
//    I added code to actually set the nStates member of the clientAtts.
//
//    Eric Brugger, Fri Jan 31 13:45:27 PST 2003
//    I removed an argument from the call to GetStateIndex.
//
//    Brad Whitlock, Mon Apr 14 17:31:12 PST 2003
//    I removed some code to update the plot list and the plot attributes
//    since that should NEVER happen inside this routine.
//
//    Eric Brugger, Fri Apr 18 12:38:05 PDT 2003
//    I added maintain view mode.
//
//    Brad Whitlock, Fri Jan 23 15:39:53 PST 2004
//    I changed the list of attributes contained by GlobalAttributes and
//    made changes here to set the right things.
//
//    Eric Brugger, Mon Mar 29 15:21:11 PST 2004
//    I added maintain data mode.
//
//    Jeremy Meredith, Wed Feb  3 15:35:08 EST 2010
//    Removed maintain data; moved maintain view from Global settings
//    (Main window) to per-window Window Information (View window).
//
// ****************************************************************************

void
ViewerWindowManager::UpdateGlobalAtts() const
{
    //
    // Update the list of sources.
    //
    GetViewerState()->GetGlobalAttributes()->SetSources(GetViewerFileServer()->GetOpenDatabases());

    //
    // Update the window list in the client Global Attributes
    // attribute subject.
    //
    int       i;
    int       activeWindowIndex;
    intVector v;

    activeWindowIndex = 0;
    for (i = 0; i < maxWindows; i++)
    {
        if (windows[i] != 0)
        {
            v.push_back(i+1);
            if (i < activeWindow)
            {
                activeWindowIndex++;
            }
        }
    }

    GetViewerState()->GetGlobalAttributes()->SetWindows(v);
    GetViewerState()->GetGlobalAttributes()->SetActiveWindow(activeWindowIndex);
    GetViewerState()->GetGlobalAttributes()->SetWindowLayout(layout);

    GetViewerState()->GetGlobalAttributes()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateViewAtts
//
//  Purpose:
//    Causes the view attributes to be sent to the viewer's client.
//
//  Arguments:
//     windowIndex   The index of the window to update view attributs for.  If
//                   this is -1, it will do it for the active window.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Jul 27 08:53:01 PDT 2001
//
//  Modifications:
//    Eric Brugger, Mon Aug 20 12:05:46 PDT 2001
//    Modified the routine to use an avtView2D to set the 2D view.  Modified
//    the routine to use an avtView3D to set the 3D view.  Removed the
//    argument from the routine.
//
//    Brad Whitlock, Mon Jan 28 16:45:46 PST 2002
//    Added code to stack the views.
//
//    Hank Childs, Mon Mar 25 14:34:13 PST 2002
//    Add support for locking views and added argument windowIndex.
//
//    Brad Whitlock, Tue Sep 17 11:43:49 PDT 2002
//    I changed the coding so the view attributes are not sent back to
//    the client unless windows are locked or the window is the active window.
//
//    Hank Childs, Fri Oct 18 15:06:45 PDT 2002
//    Fixed a bug where higher-indexed windows did not always lock into place.
//
//    Brad Whitlock, Tue Nov 19 14:39:19 PST 2002
//    Added default arguments that tell whether or not the 2d, 3d views
//    should be sent to the client.
//
//    Eric Brugger, Tue Jun 10 13:10:17 PDT 2003
//    I renamed camera to view normal in the view attributes.  I added
//    image pan and image zoom to the 3d view attributes.
//
//    Brad Whitlock, Tue Jul 1 14:07:52 PST 2003
//    I used new convenience methods for setting the viewAtts with the avt
//    view objects.
//
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I split the view attributes into 2d and 3d parts.
//
//    Mark C. Miller, Thu Jul 21 12:52:42 PDT 2005
//    Added logic to UpdateWindowInformation, win mode only so that GUI
//    would know what WINMODE the window is in
//
//    Brad Whitlock, Tue Mar 7 17:28:48 PST 2006
//    Moved code to ViewerWindow::PushViews.
//
//    Jeremy Meredith, Thu Jan 31 14:56:06 EST 2008
//    Added new axis array window mode.
//
//    Jeremy Meredith, Mon Feb  4 13:33:29 EST 2008
//    Added remaining support for axis array window modality.
//
//    Kathleen Biagas, Thu Aug 15, 2024
//    Ensure perspective (if changed) is set in WindowInformation by adding
//    WINDOWINFO_WINDOWFLAGS to the flags sent to UpdateWindowInformation.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateViewAtts(int windowIndex, bool updateCurve,
                            bool update2d, bool update3d, bool updateAxisArray)
{
    int index = (windowIndex == -1 ? activeWindow : windowIndex);
    const avtViewCurve &viewCurve = windows[index]->GetViewCurve();
    const avtView2D &view2d = windows[index]->GetView2D();
    const avtView3D &view3d = windows[index]->GetView3D();
    const avtViewAxisArray &viewAxisArray = windows[index]->GetViewAxisArray();

    if(index == activeWindow || windows[index]->GetViewIsLocked())
    {
        bool haveNotified = false;
        int flags = WINDOWINFO_WINMODEONLY;

        //
        // Set the curve attributes from the window's view.
        //
        if(updateCurve)
        {
            viewCurve.SetToViewCurveAttributes(GetViewerState()->GetViewCurveAttributes());
            GetViewerState()->GetViewCurveAttributes()->Notify();
            haveNotified = true;
        }

        //
        // Set the 2D attributes from the window's view.
        //
        if(update2d)
        {
            view2d.SetToView2DAttributes(GetViewerState()->GetView2DAttributes());
            GetViewerState()->GetView2DAttributes()->Notify();
            haveNotified = true;
        }

        //
        // Set the 3D attributes from the window's view.
        //
        if(update3d)
        {
            // ensure that if perspective has changed, the perspective flag
            // in WindowInformation will be updated as well.  This ensures the
            // toggle button on the toolbar will reflect the current state and
            // that the setting will get save/restored correctly in configs.
            if(GetViewerState()->GetWindowInformation()->GetPerspective() !=
                view3d.perspective)
                flags |= WINDOWINFO_WINDOWFLAGS;

            view3d.SetToView3DAttributes(GetViewerState()->GetView3DAttributes());
            GetViewerState()->GetView3DAttributes()->Notify();
            haveNotified = true;
        }

        //
        // Set the 3D attributes from the window's view.
        //
        if(updateAxisArray)
        {
            viewAxisArray.SetToViewAxisArrayAttributes(GetViewerState()->GetViewAxisArrayAttributes());
            GetViewerState()->GetViewAxisArrayAttributes()->Notify();
            haveNotified = true;
        }

        if(haveNotified)
            UpdateWindowInformation(flags, index);
    }

    //
    // Update the other windows if their views are locked.
    //
    if (windows[index]->GetViewIsLocked())
    {
        for (int i = 0; i < maxWindows; i++)
        {
            if (windows[i] != NULL && i != index)
            {
                if (windows[i]->GetViewIsLocked())
                {
                    windows[i]->SetViewCurve(viewCurve);
                    windows[i]->SetView2D(view2d);
                    windows[i]->SetView3D(view3d);
                    windows[i]->SetViewAxisArray(viewAxisArray);
                }
            }
        }
    }

    //
    // Stack the views.
    //
    if(viewStacking)
    {
        windows[index]->PushCurrentViews();

        // Update the actions to make sure that the undo/redo view buttons
        // in the toolbar are properly highlighted.
        windows[index]->GetActionManager()->UpdateSingleWindow();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateAnimationAtts
//
//  Purpose:
//    Sends the animation attributes for the active window to the client.
//
//  Programmer: Eric Brugger
//  Creation:   November 21, 2001
//
//  Modifications:
//    Brad Whitlock, Wed Dec 10 14:51:29 PST 2008
//    Get the animation attributes directly from the plot list.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateAnimationAtts()
{
    ViewerWindow *win = windows[activeWindow];

    //
    // Copy the window's animation attributes to the client annotation
    // attributes and notify the client.
    //
    *GetViewerState()->GetAnimationAttributes() = win->GetPlotList()->GetAnimationAttributes();
    GetViewerState()->GetAnimationAttributes()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateAnnotationAtts
//
//  Purpose:
//    Sends the annotation attributes for the active window to the client.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 09:49:04 PDT 2001
//
//  Modifications:
//   Kathleen Bonnell, Wed Sep 26 11:49:18 PDT 2001
//   Added call to SetPlotColors.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateAnnotationAtts()
{
    ViewerWindow *win = windows[activeWindow];
    const AnnotationAttributes *winAtts = win->GetAnnotationAttributes();

    SetPlotColors(winAtts);
    //
    // Copy the window's annotation attributes to the client annotation
    // attributes and notify the client.
    //
    *GetViewerState()->GetAnnotationAttributes() = *winAtts;
    GetViewerState()->GetAnnotationAttributes()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateLightListAtts
//
//  Purpose:
//    Sends the light list for the active window to the client.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Sep 14 15:24:11 PST 2001
//
//  Modifications:
//    Kathleen Bonnell, Tue Aug 13 15:15:37 PDT 2002
//    The viewer window now stores LightList attributes, not avtLightList, so
//    there is no need for the call to MakeAttributes.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateLightListAtts()
{
    //
    // Copy the window's annotation attributes to the client light list
    // and notify the client.
    //
    ViewerWindow *win = windows[activeWindow];
    const LightList *lightAtts = win->GetLightList();
    *GetViewerState()->GetLightList() = *lightAtts;
    GetViewerState()->GetLightList()->Notify();
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateRenderingAtts
//
// Purpose:
//   Sends the rendering attributes to the client.
//
// Arguments:
//   windowIndex : The index of the window to use.
//
// Programmer: Brad Whitlock
// Creation:   Thu Sep 19 13:49:10 PST 2002
//
// Modifications:
//   Kathleen Bonnell, Wed Dec  4 17:38:27 PST 2002
//   Removed antialiasing frames, no longer needed.
//
//   Jeremy Meredith, Fri Nov 14 17:44:22 PST 2003
//   Added updates for specular.
//
//   Hank Childs, Mon May 10 08:10:40 PDT 2004
//   Replace references to immediate mode rendering with display list mode.
//
//   Mark C. Miller, Tue May 11 20:21:24 PDT 2004
//   Modified calls to set scalable controls to accomdate scalable activaation
//   mode and scalable auto threshold
//
//   Hank Childs, Sun Oct 24 13:39:57 PDT 2004
//   Added updates for shading.
//
//   Brad Whitlock, Mon Sep 18 10:43:28 PDT 2006
//   Added update for color texturing.
//
//   Jeremy Meredith, Wed Aug 29 15:23:19 EDT 2007
//   Added depth cueing properties.
//
//   Jeremy Meredith, Fri Apr 30 14:39:07 EDT 2010
//   Added automatic depth cueing mode.
//
//   Eric Brugger, Fri Oct 28 10:07:28 PDT 2011
//   Add a multi resolution display capability for AMR data.
//
//   Garrett Morrison, Fri May 11 17:57:47 PDT 2018
//   Added ospray rendering properties
//
// ****************************************************************************

void
ViewerWindowManager::UpdateRenderingAtts(int windowIndex)
{
    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(index == activeWindow)
    {
        ViewerWindow *win = windows[index];

        //
        // Copy the window's rendering attributes to the client rendering
        // attributes and notify the client.
        //

        // Note: rendering attributes are percolated via the follwing path:
        // ViewerWindowManager->ViewerWindow->VisWindow->VisWinRendering
        // If new rendering attributes are introduced ALL of the above
        // classes (in multiple places) must be updated.

        GetViewerState()->GetRenderingAttributes()->SetAntialiasing(win->GetAntialiasing());
        GetViewerState()->GetRenderingAttributes()->SetMultiresolutionMode(win->GetMultiresolutionMode());
        GetViewerState()->GetRenderingAttributes()->SetMultiresolutionCellSize(win->GetMultiresolutionCellSize());
        GetViewerState()->GetRenderingAttributes()->SetGeometryRepresentation(
            (RenderingAttributes::GeometryRepresentation)win->GetSurfaceRepresentation());
        GetViewerState()->GetRenderingAttributes()->SetStereoRendering(win->GetStereo());
        GetViewerState()->GetRenderingAttributes()->SetStereoType((RenderingAttributes::StereoTypes)
            win->GetStereoType());
        GetViewerState()->GetRenderingAttributes()->SetNotifyForEachRender(win->GetNotifyForEachRender());
        GetViewerState()->GetRenderingAttributes()->SetScalableActivationMode(
            (RenderingAttributes::TriStateMode) win->GetScalableActivationMode());
        GetViewerState()->GetRenderingAttributes()->SetScalableAutoThreshold(win->GetScalableAutoThreshold());
        GetViewerState()->GetRenderingAttributes()->SetSpecularFlag(win->GetSpecularFlag());
        GetViewerState()->GetRenderingAttributes()->SetSpecularCoeff(win->GetSpecularCoeff());
        GetViewerState()->GetRenderingAttributes()->SetSpecularPower(win->GetSpecularPower());
        GetViewerState()->GetRenderingAttributes()->SetSpecularColor(win->GetSpecularColor());
        GetViewerState()->GetRenderingAttributes()->SetDoShadowing(win->GetDoShading());
        GetViewerState()->GetRenderingAttributes()->SetShadowStrength(win->GetShadingStrength());
        GetViewerState()->GetRenderingAttributes()->SetDoDepthCueing(win->GetDoDepthCueing());
        GetViewerState()->GetRenderingAttributes()->SetDepthCueingAutomatic(win->GetDepthCueingAutomatic());
        GetViewerState()->GetRenderingAttributes()->SetStartCuePoint(win->GetStartCuePoint());
        GetViewerState()->GetRenderingAttributes()->SetEndCuePoint(win->GetEndCuePoint());
        GetViewerState()->GetRenderingAttributes()->SetDepthPeeling(win->GetDepthPeeling());
        GetViewerState()->GetRenderingAttributes()->SetOcclusionRatio(win->GetOcclusionRatio());
        GetViewerState()->GetRenderingAttributes()->SetNumberOfPeels(win->GetNumberOfPeels());
        GetViewerState()->GetRenderingAttributes()->SetColorTexturingFlag(win->GetColorTexturingFlag());
#if defined(HAVE_OSPRAY)
//NOTE: win->GetOsprayRendering is returning 0 here which means that ospray is immediately getting turned off after the config setup turns it on
        GetViewerState()->GetRenderingAttributes()->SetOsprayRendering(win->GetOsprayRendering());
        GetViewerState()->GetRenderingAttributes()->SetOspraySPP(win->GetOspraySPP());
        GetViewerState()->GetRenderingAttributes()->SetOsprayAO(win->GetOsprayAO());
        GetViewerState()->GetRenderingAttributes()->SetOsprayShadows(win->GetOsprayShadows());
#endif

        // Tell the client about the new rendering information.
        GetViewerState()->GetRenderingAttributes()->Notify();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::RenderInformationCallback
//
// Purpose:
//   This callback sends rendering information back to the client.
//
// Arguments:
//   data : The index of the window that generated the render.
//
// Programmer: Brad Whitlock
// Creation:   Mon Sep 23 12:07:30 PDT 2002
//
// Modifications:
//   Brad Whitlock, Mon Sep 30 12:15:25 PDT 2002
//   Fixed problem with 64 bit to 32 bit typecast.
//
//   Brad Whitlock, Fri Jan 23 15:56:26 PST 2004
//   Made it call UpdateWindowRenderingInformation.
//
// ****************************************************************************

void
ViewerWindowManager::RenderInformationCallback(void *data)
{
    int index = *((int*)data);
    instance->UpdateWindowRenderingInformation(index);
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateAllAtts
//
// Purpose:
//   Updates all of the attributes for the window. This means that all the
//   window attributes are sent to the client.
//
// Programmer: Brad Whitlock
// Creation:   Mon Feb 4 10:25:01 PDT 2002
//
// Modifications:
//   Brad Whitlock, Mon Sep 16 15:18:58 PST 2002
//   I made the WindowInformation and the Rendering attributes update.
//
//   Brad Whitlock, Mon Nov 11 17:40:12 PST 2002
//   I added code to update the SIL restriction so the client gets the
//   right one when we change the active window.
//
//   Kathleen Bonnell, Fri Nov 15 09:07:36 PST 2002
//   Removed UpdatePickAttributes.
//
//   Brad Whitlock, Mon Apr 14 17:26:24 PST 2003
//   Factored some updates out of UpdateGlobalAtts.
//
//   Eric Brugger, Wed Oct  8 11:37:15 PDT 2003
//   I added code to turn off view stacking when calling UpdateViewAtts so
//   that a new view doesn't get pushed on the view stack.
//
//   Brad Whitlock, Fri Oct 24 17:10:07 PST 2003
//   Added code to update the expression list.
//
//   Brad Whitlock, Wed Oct 29 11:39:22 PDT 2003
//   Added code to update the annotation object list.
//
//   Brad Whitlock, Fri Jan 23 15:55:55 PST 2004
//   I split up UpdateWindowInformation into three methods.
//
//   Kathleen Bonnell, Wed Aug 18 09:28:51 PDT 2004
//   Added call to update interactor atts.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateAllAtts()
{
    //
    // Update the client global attributes.
    //
    UpdateGlobalAtts();

    //
    // Update the plot list and the plot attribute windows, and the SIL
    // restriction.
    //
    if(windows[activeWindow] != NULL)
    {
        ViewerPlotList *plotList = windows[activeWindow]->GetPlotList();
        plotList->UpdatePlotList();
        plotList->UpdatePlotAtts();
        plotList->UpdateSILRestrictionAtts();
        plotList->UpdateExpressionList(true);
        UpdateKeyframeAttributes();
        UpdateAnnotationObjectList();
    }

    //
    // Send the new view info to the client.
    //
    viewStacking = false;
    UpdateViewAtts();
    viewStacking = true;

    //
    // Update the client animation attributes.
    //
    UpdateAnimationAtts();

    //
    // Update the client annotation attributes.
    //
    UpdateAnnotationAtts();

    //
    // Update the client interactor attributes.
    //
    UpdateInteractorAtts();

    //
    // Update the client's light list.
    //
    UpdateLightListAtts();

    //
    // Update the window information.
    //
    UpdateWindowInformation(-1);
    UpdateWindowRenderingInformation();
    UpdateViewKeyframeInformation();

    //
    // Update the rendering attributes.
    //
    UpdateRenderingAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateKeyframeAttributes
//
// Purpose:
//   Sends the keyframing attributes back to the client.
//
// Programmer: Brad Whitlock
// Creation:   Wed Apr 7 00:33:10 PDT 2004
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::UpdateKeyframeAttributes()
{
    ViewerPlotList *plotList = windows[activeWindow]->GetPlotList();
    GetViewerState()->GetKeyframeAttributes()->SetEnabled(plotList->GetKeyframeMode());
    GetViewerState()->GetKeyframeAttributes()->SetNFrames(plotList->GetNKeyframes());
    GetViewerState()->GetKeyframeAttributes()->SetNFramesWasUserSet(plotList->GetNKeyframesWasUserSet());
    GetViewerState()->GetKeyframeAttributes()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetAnimationAttsFromClient
//
//  Purpose:
//    Sets the animation attributes for the active window based on the
//    client's animation attributes.
//
//  Programmer: Eric Brugger
//  Creation:   November 21, 2001
//
//  Modifications:
//    Brad Whitlock, Tue May 14 12:11:56 PDT 2002
//    Added code to update the animation timer.
//
//    Brad Whitlock, Mon Oct 6 17:04:24 PST 2003
//    Made it use SetAnimationAttributes.
//
//    Brad Whitlock, Wed Dec 10 14:47:56 PST 2008
//    Set the animation attributes directly into the plot list.
//
// ****************************************************************************

void
ViewerWindowManager::SetAnimationAttsFromClient()
{
    // Set the animation attributes using the animation atts.
    windows[activeWindow]->GetPlotList()->SetAnimationAttributes(
        *GetViewerState()->GetAnimationAttributes());

    UpdateAnimationTimer();
}

// ****************************************************************************
//  Method: ViewerWindowManager::GetAnnotationDefaultAtts
//
//  Purpose:
//    Returns a pointer to the default annotation attributes.
//
//  Returns:    A pointer to the default annotation attributes.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 08:38:30 PDT 2001
//
// ****************************************************************************

AnnotationAttributes *
ViewerWindowManager::GetAnnotationDefaultAtts()
{
    //
    // If the client attributes haven't been allocated then do so.
    //
    if (annotationDefaultAtts == 0)
    {
        annotationDefaultAtts = new AnnotationAttributes;
    }

    return annotationDefaultAtts;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetClientAnnotationAttsFromDefault
//
//  Purpose:
//    This method copies the default annotation attributes into the client
//    annotation attributes.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 09:14:46 PDT 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetClientAnnotationAttsFromDefault()
{
    if(annotationDefaultAtts != 0 && GetViewerState()->GetAnnotationAttributes() != 0)
    {
        *GetViewerState()->GetAnnotationAttributes() = *annotationDefaultAtts;
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetDefaultAnnotationAttsFromClient
//
//  Purpose:
//    This method copies the client's annotation attributes into the default
//    annotation attributes.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 09:14:46 PDT 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetDefaultAnnotationAttsFromClient()
{
    if(annotationDefaultAtts != 0 && GetViewerState()->GetAnnotationAttributes() != 0)
    {
        *annotationDefaultAtts = *GetViewerState()->GetAnnotationAttributes();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetAnnotationAttsFromClient
//
//  Purpose:
//    Sets the annotation attributes for the active window based on the
//    client's annotation attributes.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 09:39:34 PDT 2001
//
//  Modifications:
//    Kathleen Bonnell, Wed Sep 26 11:49:18 PDT 2001
//    Added call to SetPlotColors.
//
// ****************************************************************************

void
ViewerWindowManager::SetAnnotationAttsFromClient()
{
    windows[activeWindow]->SetAnnotationAttributes(GetViewerState()->GetAnnotationAttributes());
    SetPlotColors(GetViewerState()->GetAnnotationAttributes());
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetAnnotationAttsFromDefault
//
//  Purpose:
//    Sets the annotation attributes for the active window based on the
//    default annotation attributes.
//
//  Arguments:
//    windowIndex  The index of the window to whose annotation attributes
//                 are to be reset.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 09:39:34 PDT 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetAnnotationAttsFromDefault()
{
    windows[activeWindow]->SetAnnotationAttributes(annotationDefaultAtts);
    //
    // Update the client's annotation attributes
    //
    UpdateAnnotationAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::AddAnnotationObject
//
// Purpose:
//   Tells the viewer window to add a new annotation object.
//
// Arguments:
//   annotType : The type of annotation object to add.
//   annotName : The name for the new annotation object.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:33:04 PDT 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::AddAnnotationObject(int annotType, const std::string &annotName)
{
    if(windows[activeWindow] != 0)
    {
        if(windows[activeWindow]->AddAnnotationObject(annotType, annotName))
            UpdateAnnotationObjectList();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::HideActiveAnnotationObjects
//
// Purpose:
//   Hides the active annotation objects.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:34:33 PDT 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::HideActiveAnnotationObjects()
{
    if(windows[activeWindow] != 0)
    {
        windows[activeWindow]->HideActiveAnnotationObjects();
        UpdateAnnotationObjectList();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::DeleteActiveAnnotationObjects
//
// Purpose:
//   Deletes the active annotation objects.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:34:33 PDT 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::DeleteActiveAnnotationObjects()
{
    if(windows[activeWindow] != 0)
    {
        windows[activeWindow]->DeleteActiveAnnotationObjects();
        UpdateAnnotationObjectList();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::RaiseActiveAnnotationObjects
//
// Purpose:
//   Raises the active annotation objects.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:34:33 PDT 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::RaiseActiveAnnotationObjects()
{
    if(windows[activeWindow] != 0)
    {
        windows[activeWindow]->RaiseActiveAnnotationObjects();
        UpdateAnnotationObjectList();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::LowerActiveAnnotationObjects
//
// Purpose:
//   Lowers the active annotation objects.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:34:33 PDT 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::LowerActiveAnnotationObjects()
{
    if(windows[activeWindow] != 0)
    {
        windows[activeWindow]->LowerActiveAnnotationObjects();
        UpdateAnnotationObjectList();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SetAnnotationObjectOptions
//
// Purpose:
//   Tells the viewer window to set the annotation object options.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:34:33 PDT 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetAnnotationObjectOptions()
{
    if(windows[activeWindow] != 0)
        windows[activeWindow]->SetAnnotationObjectOptions(*GetViewerState()->GetAnnotationObjectList());
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateAnnotationList
//
// Purpose:
//   Sends the annotation object list for the active window back to the client.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:26:42 PDT 2003
//
// Modifications:
//   Brad Whitlock, Tue Mar 20 12:08:50 PDT 2007
//   Added delay option that will get us back here later from the event loop.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateAnnotationObjectList(bool delay)
{
    if(delay)
    {
        debug2 << "ViewerWindowManager::UpdateAnnotationObjectList: delay=true" << endl;
        GetViewerMessaging()->QueueCommand(new ViewerCommandUpdateAnnotationObjectList());
    }
    else
    {
        if(windows[activeWindow] != 0)
        {
            debug2 << "ViewerWindowManager::UpdateAnnotationObjectList: delay=false" << endl;
            windows[activeWindow]->UpdateAnnotationObjectList(*GetViewerState()->GetAnnotationObjectList());
            GetViewerState()->GetAnnotationObjectList()->Notify();
        }
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetKeyframeAttsFromClient
//
//  Purpose:
//    Sets the keyframe attributes for the active window based on the
//    client's keyframe attributes.
//
//  Programmer: Eric Brugger
//  Creation:   November 25, 2002
//
//  Modifications:
//    Brad Whitlock, Mon Jan 26 22:44:52 PST 2004
//    Made it use the plot list and I made it set the number of keyframes
//    into the plot list.
//
//    Eric Brugger, Eric Brugger, Wed Mar 22 16:23:12 PDT 2023
//    Modified where the viewer window manager keyframe and timeslider
//    attributes are updated in response to changes in the keyframe mode
//    and number of keyframes.
//
// ****************************************************************************

void
ViewerWindowManager::SetKeyframeAttsFromClient()
{
    windows[activeWindow]->GetPlotList()->SetKeyframeMode(
        GetViewerState()->GetKeyframeAttributes()->GetEnabled());
    windows[activeWindow]->GetPlotList()->SetNKeyframes(
        GetViewerState()->GetKeyframeAttributes()->GetNFrames());
    UpdateKeyframeAttributes();
    UpdateWindowInformation(WINDOWINFO_TIMESLIDERS);
}

// ****************************************************************************
// Method: ViewerWindowManager::SetFrameIndex
//
// Purpose:
//   Sets the frame index for the active window and all windows that have
//   their locktime flag set to true.
//
// Arguments:
//   frame       : The new active frame.
//   windowIndex : The index of the window for which we'll set the frame index.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 12:08:52 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 5 10:24:53 PDT 2003
//   Added windowIndex so it does not need to be called only on the active
//   window.
//
//   Eric Brugger, Mon Dec  8 08:24:16 PST 2003
//   Added code to turn on view limit merging.
//
//   Brad Whitlock, Mon Jan 26 16:50:40 PST 2004
//   Changed how animation is done.
//
//   Hank Childs, Thu Feb  7 11:12:47 PST 2008
//   Offload code for synchronizing time locked windows to the new method
//   SynchronizeTimeLockedWindows.  This way PrevFrame and NextFrame can
//   also use them.
//
// ****************************************************************************

void
ViewerWindowManager::SetFrameIndex(int state, int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        // Set the frame of the active window first.
        windows[index]->SetMergeViewLimits(true);
        ViewerPlotList *activePL = windows[index]->GetPlotList();
        activePL->SetTimeSliderState(state);

        if(windows[index]->GetTimeLock() && activePL->HasActiveTimeSlider())
        {
            SynchronizeTimeLockedWindows(windowIndex,state);
        }

        //
        // Send the new time slider state to the client.
        //
        UpdateWindowInformation(WINDOWINFO_ANIMATION);
    }
}


// ****************************************************************************
// Method: ViewerWindowManager::SynchronizeTimeLockedWindows
//
// Purpose:
//     Makes sure all of the time locked windows have a consistent state.
//
// Arguments:
//   windowIndex : The index of the window for which we'll set the frame index.
//   state       : The new active frame.
//
// Notes:      Initial code taken from method SetFrameIndex written by
//             Brad Whitlock.
//
// Programmer: Hank Childs
// Creation:   February 7, 2008
//
// Modifications:
//   Brad Whitlock, Wed Apr 30 10:07:02 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::SynchronizeTimeLockedWindows(int windowIndex, int state)
{
    if (windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if (windows[index] == NULL)
        return;
    ViewerPlotList *activePL = windows[index]->GetPlotList();
    if (!(windows[index]->GetTimeLock()) || !(activePL->HasActiveTimeSlider()))
        return;

    //
    // If the active window is time-locked, update the other windows
    // that are also time locked and have the same time slider or a
    // time slider that is used by the correlation for the active
    // window's time slider.
    //
    intVector badWindowIds;
    for (int i = 0; i < maxWindows; ++i)
    {
        if (i != index && windows[i] != 0)
        {
            ViewerPlotList *winPL = windows[i]->GetPlotList();
            if(windows[i]->GetTimeLock() &&
               winPL->HasActiveTimeSlider())
            {
                int tsState = -1;
                if (activePL->GetActiveTimeSlider() ==
                    winPL->GetActiveTimeSlider())
                {
                    //
                    // The windows have the same active time slider
                    // so we can just set the state.
                    //
                    tsState = state;
                }
                else
                {
                    //
                    // The windows have different active time sliders
                    // so let's see if we can set the time for the i'th
                    // time slider. If not, warn the user.
                    //
                    DatabaseCorrelationList *cL = GetViewerState()->
                        GetDatabaseCorrelationList();
                    DatabaseCorrelation *c = cL->FindCorrelation(
                        activePL->GetActiveTimeSlider());
                    const std::string &ts = winPL->GetActiveTimeSlider();
                    if(c != 0)
                        tsState = c->GetCorrelatedTimeState(ts, state);
                }

                if (tsState != -1)
                {
                    windows[i]->SetMergeViewLimits(true);
                    windows[i]->GetPlotList()->SetTimeSliderState(tsState);
                }
                else
                    badWindowIds.push_back(i);
            }
        }
    }

    if (badWindowIds.size() > 0)
    {
        std::string badList;
        char tmp[50];
        for (size_t j = 0; j < badWindowIds.size(); ++j)
        {
            snprintf(tmp, 50, "%d", badWindowIds[j] + 1);
            badList += tmp;
            if(j < badWindowIds.size() - 1)
                badList += ", ";
        }

        ViewerText msg(TR("VisIt did not set the time state for these windows: %1."
                         "The time slider in cannot be set by the "
                         "active window's time slider since the correlations of "
                         "the time sliders have nothing in common.\n\nTo avoid "
                         "this warning in the future, make sure that locked "
                         "windows have compatible time sliders.").
                      arg(badList));
        GetViewerMessaging()->Warning(msg);
    }
}


// ****************************************************************************
// Method: ViewerWindowManager::NextFrame
//
// Purpose:
//   Advances one frame for the active window and all windows that have
//   their locktime flag set to true.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 12:08:52 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 5 10:28:13 PDT 2003
//   Made it so it does not have to apply to the active window.
//
//   Brad Whitlock, Mon Jan 26 16:49:06 PST 2004
//   I changed how animation works.
//
//   Hank Childs, Thu Feb  7 11:12:47 PST 2008
//   Offload work of synchronizing time locked windows to the new method
//   SynchronizeTimeLockedWindows.
//
//   Brad Whitlock, Wed Dec 10 15:37:01 PST 2008
//   Use AnimationAttributes.
//
// ****************************************************************************

void
ViewerWindowManager::NextFrame(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        // Advance one frame for the active window first.
        AnimationAttributes a;
        a = windows[index]->GetPlotList()->GetAnimationAttributes();
        a.SetAnimationMode(AnimationAttributes::StopMode);
        windows[index]->GetPlotList()->SetAnimationAttributes(a);
        ViewerPlotList *activePL = windows[index]->GetPlotList();
        activePL->ForwardStep();

        // If the active window is time-locked, update the other windows
        // that are also time locked.
        if(windows[index]->GetTimeLock() && activePL->HasActiveTimeSlider())
        {
            int state = 0, nstates = 1;
            activePL->GetTimeSliderStates(activePL->GetActiveTimeSlider(),
                                          state, nstates);
            SynchronizeTimeLockedWindows(windowIndex,state);
        }

        //
        // Modify the animation timer since we set the animation mode
        // for at least one window.
        //
        UpdateAnimationTimer();
        UpdateWindowInformation(WINDOWINFO_ANIMATION);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::PrevFrame
//
// Purpose:
//   Advances one frame for the active window and all windows that have
//   their locktime flag set to true.
//
// Arguments:
//   windowIndex : The index of the window that originated the call.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 12:08:52 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 5 10:31:01 PDT 2003
//   I made it so the routine does not have to apply to the active window.
//
//   Brad Whitlock, Mon Jan 26 16:46:08 PST 2004
//   I changed how animation is done.
//
//   Hank Childs, Thu Feb  7 11:12:47 PST 2008
//   Offload work of synchronizing time locked windows to the new method
//   SynchronizeTimeLockedWindows.
//
//   Brad Whitlock, Wed Dec 10 15:37:01 PST 2008
//   Use AnimationAttributes.
//
// ****************************************************************************

void
ViewerWindowManager::PrevFrame(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        // Back up one frame for the active window first.
        AnimationAttributes a;
        a = windows[index]->GetPlotList()->GetAnimationAttributes();
        a.SetAnimationMode(AnimationAttributes::StopMode);
        windows[index]->GetPlotList()->SetAnimationAttributes(a);
        ViewerPlotList *activePL = windows[index]->GetPlotList();
        activePL->BackwardStep();

        // If the active window is time-locked, update the other windows
        // that are also time locked.
        if(windows[index]->GetTimeLock() && activePL->HasActiveTimeSlider())
        {
            int state = 0, nstates = 1;
            activePL->GetTimeSliderStates(activePL->GetActiveTimeSlider(),
                                          state, nstates);
            SynchronizeTimeLockedWindows(windowIndex,state);
        }

        //
        // Modify the animation timer since we set the animation mode
        // for at least one window.
        //
        UpdateAnimationTimer();
        UpdateWindowInformation(WINDOWINFO_ANIMATION);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::Stop
//
// Purpose:
//   Stops animation for the active window and all windows that have
//   their locktime flag set to true.
//
// Arguments:
//   windowIndex : The index of the window that originated the call.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 12:08:52 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 5 10:31:01 PDT 2003
//   I made it so the routine does not have to apply to the active window.
//
//   Brad Whitlock, Mon Jan 26 09:58:20 PDT 2004
//   I changed how animation is done.
//
//   Brad Whitlock, Wed Dec 10 15:37:01 PST 2008
//   Use AnimationAttributes.
//
//   Brad Whitlock, Mon Jul 23 11:12:35 PDT 2012
//   Update the frame in case we interrupted generation of pending plots.
//
// ****************************************************************************

void
ViewerWindowManager::Stop(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        // Stop animation for the active window first.
        AnimationAttributes a;
        a = windows[index]->GetPlotList()->GetAnimationAttributes();
        a.SetAnimationMode(AnimationAttributes::StopMode);
        windows[index]->GetPlotList()->SetAnimationAttributes(a);

        // If the active window is time-locked, update the other windows that are
        // also time locked.
        if(windows[index]->GetTimeLock())
        {
            for(int i = 0; i < maxWindows; ++i)
            {
                if(i != index && windows[i] != 0)
                {
                    if(windows[i]->GetTimeLock())
                    {
                        a = windows[i]->GetPlotList()->GetAnimationAttributes();
                        a.SetAnimationMode(AnimationAttributes::StopMode);
                        windows[i]->GetPlotList()->SetAnimationAttributes(a);
                    }
                }
            }
        }

        //
        // Modify the animation timer since we set the animation mode
        // for at least one window.
        //
        UpdateAnimationTimer();
        UpdateWindowInformation(WINDOWINFO_ANIMATION);

        //
        // Turning off the animation timer could have interrupted the generation
        // of some plots. Update the frame to make sure that any pending plots
        // get created. Note that we do a delayed update here since Stop is
        // a "special" action.
        //
        windows[index]->SendUpdateFrameMessage();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::Play
//
// Purpose:
//   Sets animation to forward play for the active window and all windows
//   that have their locktime flag set to true.
//
// Arguments:
//   windowIndex : The index of the window that originated the call.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 12:08:52 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 5 10:31:01 PDT 2003
//   I made it so the routine does not have to apply to the active window.
//
//   Brad Whitlock, Mon Jan 26 22:46:27 PST 2004
//   I changed how animation works.
//
//   Brad Whitlock, Wed Dec 10 15:37:01 PST 2008
//   Use AnimationAttributes.
//
// ****************************************************************************

void
ViewerWindowManager::Play(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        // Start forward animation for the active window first.
        AnimationAttributes a;
        a = windows[index]->GetPlotList()->GetAnimationAttributes();
        a.SetAnimationMode(AnimationAttributes::PlayMode);
        windows[index]->GetPlotList()->SetAnimationAttributes(a);

        // If the active window is time-locked, update the other windows that are
        // also time locked.
        if(windows[index]->GetTimeLock())
        {
            for(int i = 0; i < maxWindows; ++i)
            {
                if(i != index && windows[i] != 0)
                {
                    if(windows[i]->GetTimeLock())
                    {
                        a = windows[i]->GetPlotList()->GetAnimationAttributes();
                        a.SetAnimationMode(AnimationAttributes::PlayMode);
                        windows[i]->GetPlotList()->SetAnimationAttributes(a);
                    }
                }
            }
        }

        //
        // Modify the animation timer since we set the animation mode
        // for at least one window.
        //
        UpdateAnimationTimer();
        UpdateWindowInformation(WINDOWINFO_ANIMATION);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ReversePlay
//
// Purpose:
//   Sets animation to reverse play for the active window and all windows
//   that have their locktime flag set to true.
//
// Arguments:
//   windowIndex : The index of the window that originated the call.
//
// Programmer: Brad Whitlock
// Creation:   Mon Nov 11 12:08:52 PDT 2002
//
// Modifications:
//   Brad Whitlock, Wed Feb 5 10:31:01 PDT 2003
//   I made it so the routine does not have to apply to the active window.
//
//   Brad Whitlock, Mon Jan 26 22:48:30 PST 2004
//   I changed how animation works.
//
//   Brad Whitlock, Wed Dec 10 15:37:01 PST 2008
//   Use AnimationAttributes.
//
// ****************************************************************************

void
ViewerWindowManager::ReversePlay(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        // Start forward animation for the active window first.
        AnimationAttributes a;
        a = windows[index]->GetPlotList()->GetAnimationAttributes();
        a.SetAnimationMode(AnimationAttributes::ReversePlayMode);
        windows[index]->GetPlotList()->SetAnimationAttributes(a);

        // If the active window is time-locked, update the other windows
        // that are also time locked.
        if(windows[index]->GetTimeLock())
        {
            for(int i = 0; i < maxWindows; ++i)
            {
                if(i != index && windows[i] != 0)
                {
                    if(windows[i]->GetTimeLock())
                    {
                        a = windows[i]->GetPlotList()->GetAnimationAttributes();
                        a.SetAnimationMode(AnimationAttributes::ReversePlayMode);
                        windows[i]->GetPlotList()->SetAnimationAttributes(a);
                    }
                }
            }
        }

        //
        // Modify the animation timer since we set the animation mode
        // for at least one window.
        //
        UpdateAnimationTimer();
        UpdateWindowInformation(WINDOWINFO_ANIMATION);
    }
}


// ****************************************************************************
// Method: ViewerWindowManager::SetActiveTimeSlider
//
// Purpose:
//   Sets the active time slider for the specified window.
//
// Arguments:
//   ts : The time slider that we want to make active.
//   windowIndex : The index of the window to change.
//
// Programmer: Brad Whitlock
// Creation:   Sun Jan 25 02:29:13 PDT 2004
//
// Modifications:
//   Brad Whitlock, Wed Apr 7 00:48:43 PDT 2004
//   I changed the code so it allows the time slider to be set to the
//   keyframe animation time slider. There was some difficulty because it
//   is a special time slider in that it does not have a database correlation.
//
//   Brad Whitlock, Tue Mar 27 11:16:50 PDT 2007
//   Pass false for new argument of GetDatabasesForWindows to preserve old
//   behavior.
//
//   Brad Whitlock, Wed Apr 30 10:07:42 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::SetActiveTimeSlider(const std::string &ts, int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        //
        // Make sure that we can find a correlation for the desired time
        // slider or it is not a valid time slider and we should return.
        //
        DatabaseCorrelationList *cL = GetViewerState()->
            GetDatabaseCorrelationList();
        DatabaseCorrelation *correlation = cL->FindCorrelation(ts);
        bool kfTimeSlider = (ts == KF_TIME_SLIDER);
        if(!kfTimeSlider && correlation == 0)
        {
            GetViewerMessaging()->Error(
                TR("VisIt could not find a database correlation for the desired "
                   "time slider so it must not be a valid time slider."));
            return;
        }

        //
        // Set the active time slider for the active window first.
        //
        windows[index]->GetPlotList()->SetActiveTimeSlider(ts);

        //
        // If the active window is time-locked, update the other windows
        // that are also time locked.
        //
        if(windows[index]->GetTimeLock())
        {
            //
            // We're making the keyframing time slider be active return early
            // since I don't think we want to make other windows use the
            // keyframe time slider since other windows are likely to have
            // very different keyframes.
            //
            if(windows[index]->GetPlotList()->GetKeyframeMode() && kfTimeSlider)
            {
                GetViewerMessaging()->Warning(
                    TR("You've made the keyframe animation time slider be the "
                       "active time slider. Other windows that are also time "
                       "locked will not have their time sliders set to the "
                       "keyframe time slider."));
                return;
            }

            intVector badWindowIds;
            for(int i = 0; i < maxWindows; ++i)
            {
                if(i != index && windows[i] != 0 && windows[i]->GetTimeLock())
                {
                    ViewerPlotList *pl = windows[i]->GetPlotList();
                    if(pl->HasActiveTimeSlider())
                    {
                        // Get the databases used in window i.
                        intVector winId; winId.push_back(i);
                        stringVector dbs;
                        GetDatabasesForWindows(winId, dbs, false);

                        //
                        // The window has some MT databases so let's make
                        // sure that they can be handled by the new time
                        // slider. If not, tell the user about it.
                        //
                        if(dbs.size() > 0)
                        {
                            bool usedAll = true;
                            for(size_t j = 0; j < dbs.size() && usedAll; ++j)
                                usedAll &= correlation->UsesDatabase(dbs[j]);
                            if(!usedAll)
                                badWindowIds.push_back(i);
                            else
                            {
                                //
                                // Set the active time slider.
                                //
                                windows[i]->GetPlotList()->SetActiveTimeSlider(ts);
                            }
                        }
                    }
                }
            }

            //
            // If we found some windows that could not use the new time slider
            // because the correlation did not support some of their databases,
            // tell the user about it.
            //
            if(badWindowIds.size() > 0)
            {
                std::string badList;
                char tmp[50];
                for (size_t j = 0; j < badWindowIds.size(); ++j)
                {
                    snprintf(tmp, 50, "%d", badWindowIds[j] + 1);
                    badList += tmp;
                    if(j < badWindowIds.size() - 1)
                        badList += ", ";
                }

                ViewerText msg(TR("VisIt did not set the time state for these windows: %1."
                                  "The time slider in cannot be set by the "
                                  "active window's time slider since the correlations of "
                                  "the time sliders have nothing in common.\n\nTo avoid "
                                  "this warning in the future, make sure that locked "
                                  "windows have compatible time sliders.").
                              arg(badList));
                GetViewerMessaging()->Warning(msg);
            }
        }

        //
        // Modify the animation timer since we set the time slider
        // for at least one window.
        //
        UpdateAnimationTimer();
        UpdateWindowInformation(WINDOWINFO_TIMESLIDERS | WINDOWINFO_ANIMATION);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::AlterTimeSlider
//
// Purpose:
//   Forces the named time slider to be within the bounds of its database
//   correlation.
//
// Arguments:
//   ts : The name of the time slider that we're updating.
//
// Programmer: Brad Whitlock
// Creation:   Thu Feb 3 14:30:15 PST 2005
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::AlterTimeSlider(const std::string &ts)
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
        {
            // Alter the time slider. This means that we're forcing it to be
            // within the bounds of its database correlation. We prevent the
            // window from updating immediately.
            if(windows[i]->GetPlotList()->AlterTimeSlider(ts, false))
            {
                // Send a message to update the window later.
                windows[i]->SendUpdateMessage();
            }
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::CreateDatabaseCorrelation
//
// Purpose:
//   Creates a new database correlation.
//
// Arguments:
//   name         : The name of the new database correlation.
//   dbs          : The databases to include in the new database correlation.
//   method       : The correlation method.
//   initialState : The initial state for the time slider that will get
//                  created to
//   nStates      : The number of states in the correlation (currently ignored).
//
// Programmer: Brad Whitlock
// Creation:   Mon Mar 29 11:21:29 PDT 2004
//
// Modifications:
//   Eric Brugger, Fri Apr 16 18:43:42 PDT 2004
//   I modified the logic concerning the creation of a new time slider.
//
//   Brad Whitlock, Mon Apr 19 08:46:00 PDT 2004
//   I removed the code to update the window information. I moved it up
//   a level so the caller of this method is now responsible for updating
//   the window information.
//
// ****************************************************************************

void
ViewerWindowManager::CreateDatabaseCorrelation(const std::string &name,
    const stringVector &dbs, int method, int initialState, int nStates)
{
    ViewerFileServerInterface *fs = GetViewerFileServer();
    DatabaseCorrelationList *cL = GetViewerState()->GetDatabaseCorrelationList();

    // Make sure that the correlation is not already in the list.
    if(cL->FindCorrelation(name) == 0)
    {
        //
        // Try and create a new correlation.
        //
        DatabaseCorrelation *correlation = GetViewerStateManager()->
            GetDatabaseCorrelationMethods()->CreateDatabaseCorrelation(
                name, dbs, method, nStates);

        //
        // If there was no error in creating the correlation then
        // add the new one to the list and notify the client.
        //
        if(correlation)
        {
            cL->AddCorrelations(*correlation);
            cL->Notify();

            //
            // Print the correlation to the log
            //
            debug3 << "New correlation:" << endl
                   << *correlation << endl;

            //
            // Now that the correlation has been created, create a time
            // slider for it in all windows and make it the active time
            // slider in the active window and all windows that are
            // locked to it.
            //
            for(int i = 0; i < maxWindows; ++i)
            {
                ViewerWindow *win = windows[i];
                if(win)
                {
                    bool timeLocked = (windows[activeWindow]->GetTimeLock() &&
                                       windows[i]->GetTimeLock());

                    bool timeSliderExists =
                        win->GetPlotList()->TimeSliderExists(name);

                    //
                    // If we didn't have a time slider, create one. Call
                    // CreateTimeSlider in any case if we were locked in time
                    // since reopening one time locked window should update
                    // others, especially if we're changing the active time
                    // slider later.
                    if(!timeSliderExists || timeLocked)
                    {
                        win->GetPlotList()->CreateTimeSlider(name,
                            initialState);
                    }

                    // Make the new correlation be the active time slider
                    // in the active window or any window locked to it.
                    if(i == activeWindow || timeLocked)
                    {
                        win->GetPlotList()->SetActiveTimeSlider(name);
                    }
                }
            }

            delete correlation;
        }
    }
    else if(!fs->IsDatabase(name))
    {
        // The database correlation is already in the list of correlations
        // so try and alter the existing correlation.
        AlterDatabaseCorrelation(name, dbs, method, nStates);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::AlterDatabaseCorrelation
//
// Purpose:
//   Alters the named database correlation.
//
// Arguments:
//   name         : The name of the new database correlation.
//   dbs          : The databases to include in the new database correlation.
//   method       : The correlation method.
//   initialState : The initial state for the time slider that will get
//                  created to
//   nStates      : The number of states in the correlation (currently ignored).
//
// Returns:
//
// Note:       Trivial correlations can't be modified.
//
// Programmer: Brad Whitlock
// Creation:   Mon Mar 29 11:23:35 PDT 2004
//
// Modifications:
//   Brad Whitlock, Mon Apr 19 08:50:47 PDT 2004
//   I removed the code to update the window information since it is already
//   done by the caller and the update here caused 2 updates.
//
//   Brad Whitlock, Wed Apr 30 10:09:50 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::AlterDatabaseCorrelation(const std::string &name,
    const stringVector &dbs, int method, int nStates)
{
    ViewerFileServerInterface *fs = GetViewerFileServer();
    DatabaseCorrelationList *cL = GetViewerState()->GetDatabaseCorrelationList();
    DatabaseCorrelation *correlation = cL->FindCorrelation(name);
    if(correlation)
    {
        // Make sure that the correlation does not have the same name as
        // an existing source.
        if(fs->IsDatabase(name))
        {
            GetViewerMessaging()->Error(
                TR("You cannot alter a database correlation that "
                   "corresponds directly to a database."));
        }
        else
        {
            //
            // Try and create a new correlation.
            //
            DatabaseCorrelation *newCorrelation = GetViewerStateManager()->
                GetDatabaseCorrelationMethods()->CreateDatabaseCorrelation(
                    name, dbs, method, nStates);

            //
            // If there was no error in creating the correlation then
            // add the new one to the list and notify the client.
            //
            if(newCorrelation)
            {
                *correlation = *newCorrelation;
                cL->Notify();

                // Make sure that all time sliders for all windows with the
                // same name as the correlation have a state that still
                // fits into the number of states for the altered correlation.
                for(int j = 0; j < maxWindows; ++j)
                {
                    if(windows[j] != 0)
                    {
                        windows[j]->GetPlotList()->
                            AlterTimeSlider(name);
                    }
                }

                delete newCorrelation;
            }
        }
    }
    else
    {
        ViewerText msg(TR("You cannot alter a database correlation for %1 "
                          "because there is no such database correlation.").
                      arg(name));
        GetViewerMessaging()->Error(msg);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::DeleteDatabaseCorrelation
//
// Purpose:
//   Deletes the named database correlation.
//
// Arguments:
//   name : The name of the correlation to delete.
//
// Programmer: Brad Whitlock
// Creation:   Mon Mar 29 11:24:10 PDT 2004
//
// Modifications:
//   Brad Whitlock, Wed Apr 30 10:10:07 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

void
ViewerWindowManager::DeleteDatabaseCorrelation(const std::string &name)
{
    ViewerFileServerInterface *fs = GetViewerFileServer();
    DatabaseCorrelationList *cL = GetViewerState()->GetDatabaseCorrelationList();
    DatabaseCorrelation *correlation = cL->FindCorrelation(name);
    if(correlation)
    {
        // Make sure that the correlation does not have the same name as
        // an existing source.
        if(fs->IsDatabase(name))
        {
            GetViewerMessaging()->Error(
                TR("You cannot delete a database correlation that "
                   "corresponds directly to a source."));
        }
        else
        {
            //
            // Remove the correlation.
            //
            for(int i = 0; i < cL->GetNumCorrelations(); ++i)
            {
                if(cL->GetCorrelations(i).GetName() == name)
                {
                    cL->RemoveCorrelations(i);
                    cL->Notify();

                    // Make any plot lists that used the correlation that
                    // we just deleted switch to a new time slider.
                    for(int j = 0; j < maxWindows; ++j)
                    {
                        if(windows[j] != 0)
                        {
                            windows[j]->GetPlotList()->
                                DeleteTimeSlider(name);
                        }
                    }

                    // The time sliders changed. Send the new list to
                    // the client.
                    UpdateWindowInformation(WINDOWINFO_TIMESLIDERS);

                    break;
                }
            }
        }
    }
    else
    {
        GetViewerMessaging()->Error(TR("There is no such database correlation."));
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::GetDatabasesForWindows
//
// Purpose:
//   Returns a list of databases for the specified windows.
//
// Arguments:
//   windowIds       : The list of windows for which we want databases.
//   dbs             : The return list of databases.
//   addAllDatabases : Adds even the ST databases.
//
// Programmer: Brad Whitlock
// Creation:   Tue Mar 16 09:01:45 PDT 2004
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::GetDatabasesForWindows(const intVector &windowIds,
    stringVector &dbs, bool addAllDatabases) const
{
    DatabaseCorrelationList *cL = GetViewerState()->GetDatabaseCorrelationList();

    for(size_t i = 0; i < windowIds.size(); ++i)
    {
        int index = windowIds[i];
        if(index >= 0 && index < maxWindows && windows[index] != 0)
        {
            ViewerPlotList *pl = windows[index]->GetPlotList();

            // Try and add the active source for the window.
            std::string source(pl->GetHostDatabaseName());
            if(addAllDatabases || cL->FindCorrelation(source) != 0)
            {
                if(std::find(dbs.begin(), dbs.end(), source) == dbs.end())
                    dbs.push_back(source);
            }

            // Try and add the source for each of the plots.
            for(int j = 0; j < pl->GetNumPlots(); ++j)
            {
                std::string pSource(pl->GetPlot(j)->GetSource());
                if(addAllDatabases || cL->FindCorrelation(pSource) != 0)
                {
                    if(std::find(dbs.begin(), dbs.end(), pSource) == dbs.end())
                        dbs.push_back(pSource);
                }
            }
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::CloseDatabase
//
// Purpose:
//   This method closes a database in all plot lists that have that database
//   as their open database. It also removes the time slider for that database
//   from all windows and clears the metadata for the database in the file
//   before finally telling the engine to clear out all networks involving
//   the database.
//
// Arguments:
//   dbName : The name of the databsae to close.
//
// Programmer: Brad Whitlock
// Creation:   Fri Feb 27 12:52:19 PDT 2004
//
// Modifications:
//    Jeremy Meredith, Tue Mar 30 11:04:04 PST 2004
//    Added an engine key used to index (and restart) engines.
//    I had to get the metadata before closing the database to determine
//    which engine the file lives on.
//
//    Brad Whitlock, Fri Feb 4 08:17:31 PDT 2005
//    Added code to tell the mdserver to close its database.
//
//    Brad Whitlock, Tue Feb 22 14:37:14 PST 2005
//    Guarded against a NULL metadata pointer.
//
//    Brad Whitlock, Wed Apr 30 10:10:43 PDT 2008
//    Support for internationalization.
//
//   Jeremy Meredith, Fri Jan 29 10:25:16 EST 2010
//   Added extra flag to tell ClearFile whether or not we want to
//   forget which plugin was used to open it.  In this case, we DO.
//
// ****************************************************************************

void
ViewerWindowManager::CloseDatabase(const std::string &dbName)
{
    //
    // Expand the filename in case it contains relative paths, etc.
    //
    ViewerFileServerInterface *fs = GetViewerFileServer();
    std::string expandedDB(dbName), host, db;
    fs->ExpandDatabaseName(expandedDB, host, db);

    std::string sim = "";
    const avtDatabaseMetaData *md = fs->GetMetaData(host, db);
    if (md != 0 && md->GetIsSimulation())
        sim = db;

    if(FileInUse(host, db))
    {
        ViewerText msg(TR("VisIt could not close \"%1\" because it is still "
                          "being used by one or more plots.").
                       arg(expandedDB));
        GetViewerMessaging()->Error(msg);
    }
    else
    {
        //
        // Tell the file server to clear out any metadata related to
        // the database that we're closing. This also deletes the database
        // correlation.
        //
        // We want to forget which plugin was used here.
        fs->ClearFile(expandedDB, true);
        GetViewerStateManager()->GetDatabaseCorrelationMethods()->
            RemoveDatabaseCorrelation(expandedDB);

        //
        // Tell the mdserver to close its database so the next time we ask
        // for it, it will give back the right information if the database
        // was a virtual database.
        //
        fs->CloseFile(host, db);

        //
        // If the plot list's open database is the database that we're
        // closing, make it have no open database. Also, remove its
        // time slider for the database
        //
        int updateFlags = 0;
        for(int i = 0; i < maxWindows; ++i)
        {
            if(windows[i] != 0)
            {
                ViewerPlotList *pl = windows[i]->GetPlotList();
                int flags = pl->CloseDatabase(expandedDB);
                if(i == activeWindow)
                    updateFlags |= flags;
            }
        }

        // Update the client if there were changes.
        if(updateFlags > 0)
            UpdateWindowInformation(updateFlags);

        //
        // Update the global attributes since clearing out the file in
        // the file server will affect the list of sources.
        //
        UpdateGlobalAtts();

        //
        // Tell the engine to clear any networks that involve the
        // database that we're closing.
        //
        GetViewerEngineManager()->ClearCache(EngineKey(host, sim), db);

        GetViewerMessaging()->Message(TR("VisIt closed \"%1\".").arg(expandedDB));
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ReplaceDatabase
//
// Purpose:
//   Replaces the database in all windows.
//
// Arguments:
//   key             : The key of the engine that will generate the plot.
//   database        : The database to use.
//   timeState       : The time state to use.
//   setTimeState    : Whether or not to set the time state.
//   onlyReplaceSame : If true, then file replacement is only done if the
//                     new database is the same as the database in a plot.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 15 14:03:35 PST 2003
//
// Modifications:
//   Brad Whitlock, Mon Nov 3 10:03:00 PDT 2003
//   Added timeState and setTimeState arguments.
//
//   Brad Whitlock, Mon Jan 26 22:50:56 PST 2004
//   I made it use the plot list directly.
//
//   Brad Whitlock, Mon May 3 12:48:29 PDT 2004
//   I made it use an engine key.
//
//   Mark C. Miller, Mon Dec  6 20:18:43 PST 2004
//   Added code to push final SIL controls to client
//
//    Cyrus Harrison, Tue Apr 14 13:35:54 PDT 2009
//    Changed the interface to ReplaceDatabase, adding option to replace
//    only active plots.
//
// ****************************************************************************

void
ViewerWindowManager::ReplaceDatabase(const EngineKey &key,
    const std::string &database, int timeState, bool setTimeState,
    bool onlyReplaceSame,bool onlyReplaceActive)
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
        {
            windows[i]->GetPlotList()->
                ReplaceDatabase(key, database, timeState, setTimeState,
                                onlyReplaceSame,
                                onlyReplaceActive);
        }
    }

    //
    // After we've completed the replace, we need to make sure the SIL
    // window displays the correct contents; that of the active plot of
    // the active window
    //
    ViewerWindow *aWin = GetActiveWindow();
    if (aWin != 0)
    {
        aWin->GetPlotList()->UpdateSILRestrictionAtts();
    }

}

// ****************************************************************************
// Method: ViewerWindowManager::CheckForNewStates
//
// Purpose:
//   Updates the correlations involving the specified database, updates any
//   time sliders, and finally resizes any actor caches if we added states.
//
// Arguments:
//   hostDatabase : The name of the database to check for new states.
//
// Programmer: Brad Whitlock
// Creation:   Tue Jul 27 10:19:17 PDT 2004
//
// Modifications:
//   Brad Whitlock, Mon Feb 11 17:10:16 PST 2008
//   Fixed call to CloseFile so it passes the name of the database to close
//   on the mdserver so the database gets re-read.
//
//   Jeremy Meredith, Fri Jan 29 10:25:16 EST 2010
//   Added extra flag to tell ClearFile whether or not we want to
//   forget which plugin was used to open it.  In this case, we do not.
//
// ****************************************************************************

void
ViewerWindowManager::CheckForNewStates(const std::string &hostDatabase)
{
    const char *mName = "ViewerWindowManager::CheckForNewStates: ";

    std::string hDB(hostDatabase);
    std::string host, db;
    ViewerFileServerInterface *fs = GetViewerFileServer();
    fs->ExpandDatabaseName(hDB, host, db);

    //
    // Determine if VisIt has even opened the specified database.
    //
    stringVector sources(fs->GetOpenDatabases());
    bool usingDatabase = false;
    for(size_t i = 0; i < sources.size() && !usingDatabase; ++i)
    {
        debug4 << "source[" << i << "] = " << sources[i].c_str() << endl;
        usingDatabase |= (sources[i] == hDB);
    }

    //
    // If VisIt is using the database then we must determine if the number of
    // time states has changed. If the number of time states has changed then
    // we need to update the database correlations and plot cache sizes.
    //
    if(usingDatabase)
    {
        const avtDatabaseMetaData *md = fs->GetMetaData(host, db);
        if(md != 0 && md->GetIsVirtualDatabase())
        {
            // Get the list of time states.
            int originalNStates = md->GetNumStates();
            stringVector originalTSNames(md->GetTimeStepNames());
            bool isSim = md->GetIsSimulation();

            debug4 << mName << hDB.c_str() << " is a virtual database with "
                   << originalNStates << " time states." << endl;

            debug4 << mName << "Current time steps for " << hDB.c_str()
                   << ": " << endl;
            for(size_t i = 0; i < originalTSNames.size(); ++i)
                debug4 << originalTSNames[i].c_str() << ", " << endl;
            debug4 << endl;

            //
            // Look through all of the windows and figure out the last time
            // state from the database that we're essentially reopening.
            //
            int timeState = 0;
            for(int i = 0; i < maxWindows; ++i)
            {
                if(windows[i] != 0)
                {
                    ViewerPlotList *pl = windows[i]->GetPlotList();
                    for(int p = 0; p < pl->GetNumPlots(); ++p)
                    {
                        ViewerPlot *plot = pl->GetPlot(p);
                        if(plot->GetSource() == hDB)
                        {
                            timeState = (timeState < plot->GetState()) ?
                                plot->GetState() : timeState;
                        }
                    }
                }
            }

            // Clear all knowledge of the file from the cache
            fs->CloseFile(host, db);
            // Don't forget which plugin you used, though.
            fs->ClearFile(hDB, false);
            GetViewerStateManager()->GetDatabaseCorrelationMethods()->
                RemoveDatabaseCorrelation(hDB);

            // Get the file's metadata again.
            debug4 << mName << "Reopening " << hDB.c_str()
                   << " at state " << timeState << endl;
            md = fs->GetMetaDataForState(host, db, timeState);
            if(md != 0 && md->GetIsVirtualDatabase())
            {
                int newNStates = md->GetNumStates();
                stringVector newTSNames(md->GetTimeStepNames());

                debug4 << mName << "New time steps for " << hDB.c_str()
                       << ": " << endl;
                for(size_t i = 0; i < newTSNames.size(); ++i)
                    debug4 << newTSNames[i].c_str() << ", " << endl;
                debug4 << endl;

                //
                // Alter any database correlations that use the database that
                // changed.
                //
                DatabaseCorrelationList *cL = GetViewerState()->GetDatabaseCorrelationList();
                stringVector alteredCorrelations;
                for(int i = 0; i < cL->GetNumCorrelations(); ++i)
                {
                    DatabaseCorrelation &correlation = cL->operator[](i);
                    if(correlation.UsesDatabase(hDB))
                    {
                        if(originalNStates != newNStates ||
                           correlation.GetMethod() == DatabaseCorrelation::TimeCorrelation ||
                           correlation.GetMethod() == DatabaseCorrelation::CycleCorrelation)
                        {
                            debug4 << mName << "Updated correlation for "
                                   << correlation.GetName().c_str() << endl;
                            alteredCorrelations.push_back(correlation.GetName());
                            GetViewerStateManager()->GetDatabaseCorrelationMethods()->
                                UpdateDatabaseCorrelation(correlation.GetName());
                        }
                    }
                }

                //
                // The previous call to FileServerList::ClearFile deleted the
                // trivial database correlation. Recreate it.
                //
                stringVector dbs; dbs.push_back(hDB);
                DatabaseCorrelation *newCorr = GetViewerStateManager()->
                    GetDatabaseCorrelationMethods()->CreateDatabaseCorrelation(hDB, dbs, 0);
                if(newCorr != 0)
                {
                    debug4 << "New correlation for " << hDB.c_str() << *newCorr << endl;
                    cL->AddCorrelations(*newCorr);
                    delete newCorr;
                }
                if(originalNStates != newNStates)
                {
                    debug4 << mName << "Updated correlation for "
                           << hDB.c_str() << endl;
                    alteredCorrelations.push_back(hDB);
                }
                // Send new database correlation list to the client.
                cL->Notify();

                //
                // If the virtual database has 1 time state per file then it
                // might be possible to expand or contract the time sliders
                // and cache sizes.
                //
                bool clearCache = true;
                bool oneTSPerFile = (originalTSNames.size() == (size_t)originalNStates &&
                   newTSNames.size() == (size_t)newNStates);
                if(oneTSPerFile)
                {
                    int n = static_cast<int>((originalTSNames.size() < newTSNames.size()) ?
                    originalTSNames.size() : newTSNames.size());
                    bool same = true;
                    for(int i = 0; i < n && same; ++i)
                        same &= (originalTSNames[i] == newTSNames[i]);
                    clearCache = !same;
                    debug4 << mName << "Need to clear actor cache: "
                           << (clearCache ? "true":"false") << endl;
                }

                //
                // Clear the cache for the database on the compute engine.
                //
                bool ntsChanged = (newNStates != originalNStates);
                if(ntsChanged)
                {
                    EngineKey key(host, "");
                    if (isSim)
                        key = EngineKey(host, db);
                    debug4 << mName << "Clearing out cached database ";
                    if(isSim)
                        debug4 << " in simulation on " << host.c_str() << endl;
                    else
                    {
                        debug4 << db.c_str() << " in compute engine on "
                               << host.c_str() << endl;
                    }
                    GetViewerEngineManager()->ClearCache(key, db);
                }

                //
                // Loop over the windows and make sure that their time sliders are
                // within the limits for the new version of the database. Also be
                // sure to adjust their cache sizes if needed.
                //
                bool expressionListUpdated = false;
                for(int i = 0; i < maxWindows; ++i)
                {
                    if(windows[i] != 0)
                    {
                        ViewerPlotList *pl = windows[i]->GetPlotList();
                        int flag = pl->ResizeTimeSliders(alteredCorrelations, clearCache);
                        bool tsSizeChanged = (flag & 1) > 0;
                        bool actorsCleared = (flag & 2) > 0;

                        // Update the time slider states for the active window
                        if((tsSizeChanged || ntsChanged) && i == activeWindow)
                            UpdateWindowInformation(WINDOWINFO_TIMESLIDERS);

                        // If we had to clear the cache then we need to update the window too.
                        if(actorsCleared)
                        {
                            // Update the expression list based on the plots. We have
                            // to update the expression list or risk not being able to
                            // update plots that have
                            expressionListUpdated = true;
                            pl->UpdateExpressionList(true, false);

                            // Update the frame.
                            debug4 << mName << "Updating window "
                                   << windows[i]->GetWindowId() << endl;
                            pl->UpdateFrame(false);
                        }
                    }
                }

                // If we updated an expression then we need to update the expression
                // list to set it back to what it was.
                if(expressionListUpdated)
                    windows[activeWindow]->GetPlotList()->UpdateExpressionList(true);
            }
            else
            {
                debug4 << mName << "Could not get the metadata for " << hDB.c_str()
                       << "or we got it and it was not a virtual database." << endl;
            }
        }
        else
        {
            debug4 << mName << hDB.c_str() << " is not a virtual database." << endl;
        }
    }
    else
    {
        debug4 << mName << hDB.c_str() << " is not an open source." << endl;
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::ResetNetworkIds
//
// Purpose:
//   Resets the network ids for all plots that use the specified engine key.
//
// Arguments:
//   key : The engine key that's getting reset.
//
// Programmer: Brad Whitlock
// Creation:   Mon May 3 14:12:42 PST 2004
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::ResetNetworkIds(const EngineKey &key)
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
            windows[i]->GetPlotList()->ResetNetworkIds(key);
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::GetLightListDefaultAtts
//
//  Purpose:
//    Returns a pointer to the default light list.
//
//  Returns:    A pointer to the default light list.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Sep 14 15:18:09 PST 2001
//
// ****************************************************************************

LightList *
ViewerWindowManager::GetLightListDefaultAtts()
{
    //
    // If the default attributes haven't been allocated then do so.
    //
    if (lightListDefaultAtts == 0)
    {
        lightListDefaultAtts = new LightList;
    }

    return lightListDefaultAtts;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetClientLightListFromDefault
//
//  Purpose:
//    This method copies the default light list into the client light list.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Sep 14 15:17:16 PST 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetClientLightListFromDefault()
{
    if(lightListDefaultAtts != 0 && GetViewerState()->GetLightList() != 0)
    {
        *GetViewerState()->GetLightList() = *lightListDefaultAtts;
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetDefaultLightListFromClient
//
//  Purpose:
//    This method copies the client's light list into the default light list.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Sep 14 15:16:25 PST 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetDefaultLightListFromClient()
{
    if(lightListDefaultAtts != 0 && GetViewerState()->GetLightList() != 0)
    {
        *lightListDefaultAtts = *GetViewerState()->GetLightList();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetLightListFromClient
//
//  Purpose:
//    Sets the light list for the active window based on the client's
//    light list.
//
//  Programmer: Brad Whitlock
//  Creation:   Thu Aug 30 09:39:34 PDT 2001
//
//  Modifications:
//    Kathleen Bonnell, Tue Aug 13 15:15:37 PDT 2002
//    The viewer window now stores LightList attributes, not avtLightList, so
//    there is no need for a conversion here.
//
//    Jeremy Meredith, Thu Oct  2 12:34:00 PDT 2003
//    Copy the light list to avtCallback.
//
// ****************************************************************************

void
ViewerWindowManager::SetLightListFromClient()
{
    // HACK: set the light list in the callback
    // for why this is bad, see VisIt00003853.
    avtCallback::SetCurrentLightList(*GetViewerState()->GetLightList());

    windows[activeWindow]->SetLightList(GetViewerState()->GetLightList());
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetLightListFromDefault
//
//  Purpose:
//    Sets the light list for the active window based on the default
//    light list.
//
//  Arguments:
//    windowIndex : The index of the window to whose light list is to be reset.
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Sep 14 15:14:11 PST 2001
//
//  Modifications:
//    Kathleen Bonnell, Tue Aug 13 15:15:37 PDT 2002
//    The viewer window now stores LightList attributes, not avtLightList, so
//    there is no need for a conversion here.
//
// ****************************************************************************

void
ViewerWindowManager::SetLightListFromDefault()
{
    windows[activeWindow]->SetLightList(lightListDefaultAtts);

    //
    // Update the client's light list.
    //
    UpdateLightListAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::GetWindowAtts
//
// Purpose:
//   Returns a pointer the ViewerWindowManagerAttributes object.
//
// Returns:    A pointer the ViewerWindowManagerAttributes object.
//
// Programmer: Brad Whitlock
// Creation:   Fri Nov 2 10:35:49 PDT 2001
//
// Modifications:
//
// ****************************************************************************

ViewerWindowManagerAttributes *
ViewerWindowManager::GetWindowAtts()
{
    if(windowAtts == 0)
    {
        windowAtts = new ViewerWindowManagerAttributes;
    }

    return windowAtts;
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateWindowAtts
//
// Purpose:
//   Stores the current viewer window locations, sizes into the
//   ViewerWindowManagerAttributes object so that information can be saved to
//   the configuration file.
//
// Programmer: Brad Whitlock
// Creation:   Fri Nov 2 10:37:06 PDT 2001
//
// Modifications:
//   Brad Whitlock, Tue Feb 5 09:53:02 PDT 2002
//   Added code to save the window's navigate mode, etc.
//
//   Brad Whitlock, Mon Sep 16 14:56:57 PST 2002
//   I removed the code to save the layout since it is now part of the
//   global atts instead.
//
//   Brad Whitlock, Wed Feb 5 14:22:29 PST 2003
//   I added support for saving toolbar settings.
//
//   Brad Whitlock, Wed Jul 23 13:57:48 PST 2003
//   I removed the window size, location from windowAtts.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateWindowAtts()
{
    // Make sure the ViewerWindowManager atts object is created.
    GetWindowAtts();

    // Let the active window's action manager update the window atts so we
    // save the current toolbar settings.
    windows[activeWindow]->GetActionManager()->SaveActionGroups(windowAtts);
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateWindowInformation
//
// Purpose:
//   Sends the window information (button state, etc.) to the client.
//
// Arguments:
//   flags       : The pieces of the window information that we want to
//                 send. See ViewerWindowManager.h for the flags that we use.
//   windowIndex : The index of the window for which we're updating. We use
//                 this to make sure that we only update the for the active
//                 window.
//
// Programmer: Brad Whitlock
// Creation:   Mon Sep 16 15:14:23 PST 2002
//
// Modifications:
//   Brad Whitlock, Mon Nov 11 11:36:43 PDT 2002
//   I added code to update the new lockTools and lockTime fields.
//
//   Eric Brugger, Tue Jan 14 08:11:52 PST 2003
//   I added the number of dimensions to the GetExtents call.
//
//   Jeremy Meredith, Tue Feb  4 17:44:20 PST 2003
//   I added the camera view mode info to the WindowInformation.
//
//   Eric Brugger, Fri Apr 18 12:38:05 PDT 2003
//   I removed auto center view.
//
//   Brad Whitlock, Wed May 21 07:50:49 PDT 2003
//   I added fullframe to the WindowInformation.
//
//   Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//   I split the view attributes into 2d and 3d parts.
//
//   Brad Whitlock, Fri Jan 23 16:10:38 PST 2004
//   I removed some code into other methods and I added code to set new
//   attributes in the WindowInformation such as the active source and the
//   list of time states. Finally, I added the flags argument so we can
//   do partial sends of data to the client.
//
//   Mark C. Miller, Thu Jul 21 12:52:42 PDT 2005
//   Fixed problem where GetViewerState()->GetWindowInformation()->SetWindowMode was called twice, once
//   with interaction mode and once with window mode. Added calls to
//   GetViewerState()->GetWindowInformation()->SetWinMode()
//
//   Brad Whitlock, Fri Jan 6 11:15:25 PDT 2006
//   Added code to set the view dimension since it was not getting set.
//
//   Jeremy Meredith, Thu Jan 31 14:56:06 EST 2008
//   Added new axis array window mode.
//
//   Brad Whitlock, Wed Dec 10 16:23:16 PST 2008
//   Update the AnimationAttributes.
//
//   Jeremy Meredith, Tue Feb  2 13:52:37 EST 2010
//   Update the tool update mode settings.
//
//   Jeremy Meredith, Wed Feb  3 15:35:08 EST 2010
//   Removed maintain data; moved maintain view from Global settings
//   (Main window) to per-window Window Information (View window).
//
//   Hank Childs, Sat Mar 13 18:46:54 PST 2010
//   Remove reference to bounding box mode.
//
//   Jonathan Byrd (Allinea Software), Sun Dec 18, 2011
//   Include the DDT connection status and whether simulation is
//   ddtsim-based in the windowInfo
//
//   Jonathan Byrd (Allinea Software), Sun Dec 18, 2011
//   Allow use of animation controls to control ddtsim-base simulations
//
// ****************************************************************************

void
ViewerWindowManager::UpdateWindowInformation(int flags, int windowIndex)
{
    //
    // Set certain window mode information into the state object.
    //
    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    ViewerWindow *win = windows[index];
    if(win != 0 && index == activeWindow)
    {
        //
        // Set the active source for the window into the window atts.
        //
        ViewerPlotList  *plotList = win->GetPlotList();
        if((flags & WINDOWINFO_SOURCE) != 0)
        {
            if(plotList->GetHostDatabaseName().empty())
                GetViewerState()->GetWindowInformation()->SetActiveSource("notset");
            else
                GetViewerState()->GetWindowInformation()->SetActiveSource(plotList->GetHostDatabaseName());
        }

        //
        // Get the list of time sliders and their current states from
        // the plot list.
        //
        stringVector timeSliders;
        intVector    timeSliderCurrentStates;
        int          activeTimeSlider = -1;
        plotList->GetTimeSliderInformation(activeTimeSlider, timeSliders,
                                           timeSliderCurrentStates);
        if((flags & WINDOWINFO_TIMESLIDERS) != 0)
        {
            GetViewerState()->GetWindowInformation()->SetTimeSliders(timeSliders);
            GetViewerState()->GetWindowInformation()->SetActiveTimeSlider(activeTimeSlider);
        }

        //
        // Return the current animation mode for the window.
        // (i.e. is the window playing an animation?)
        //
        if((flags & WINDOWINFO_ANIMATION) != 0 ||
           (flags & WINDOWINFO_TIMESLIDERS) != 0)
        {
            GetViewerState()->GetWindowInformation()->SetTimeSliderCurrentStates(timeSliderCurrentStates);

            AnimationAttributes::AnimationMode m;
            m = plotList->GetAnimationAttributes().GetAnimationMode();
            if (m == AnimationAttributes::PlayMode)
                GetViewerState()->GetWindowInformation()->SetAnimationMode(3);
            else if (m == AnimationAttributes::ReversePlayMode)
                GetViewerState()->GetWindowInformation()->SetAnimationMode(1);
            else
                GetViewerState()->GetWindowInformation()->SetAnimationMode(2);

            // Update the client animation atts too.
            UpdateAnimationAtts();
        }

        //
        // Set window mode, etc.
        //
        if((flags & WINDOWINFO_WINDOWFLAGS) != 0)
        {
            GetViewerState()->GetWindowInformation()->SetInteractionMode(int(win->GetInteractionMode()));
            GetViewerState()->GetWindowInformation()->SetToolUpdateMode(int(win->GetToolUpdateMode()));
            GetViewerState()->GetWindowInformation()->SetSpin(win->GetSpinMode());
            GetViewerState()->GetWindowInformation()->SetLockView(win->GetViewIsLocked());
            GetViewerState()->GetWindowInformation()->SetViewExtentsType(int(win->GetViewExtentsType()));
            GetViewerState()->GetWindowInformation()->SetWinMode(win->GetWindowMode());
            GetViewerState()->GetWindowInformation()->SetPerspective(win->GetPerspectiveProjection());
            GetViewerState()->GetWindowInformation()->SetLockTools(win->GetToolLock());
            GetViewerState()->GetWindowInformation()->SetLockTime(win->GetTimeLock());
            GetViewerState()->GetWindowInformation()->SetCameraViewMode(win->GetCameraViewMode());
            GetViewerState()->GetWindowInformation()->SetFullFrame(win->GetFullFrameMode());
            GetViewerState()->GetWindowInformation()->SetMaintainView(win->GetMaintainViewMode());

            // indicate if we're in scalable rendering mode
            GetViewerState()->GetWindowInformation()->SetUsingScalableRendering(win->GetScalableRendering());
        }

        //
        // Set WINMODE only
        //
        if ((flags & WINDOWINFO_WINMODEONLY) != 0)
            GetViewerState()->GetWindowInformation()->SetWinMode(win->GetWindowMode());

        //
        // Get the window size.
        //
        if((flags & WINDOWINFO_WINDOWSIZE) != 0)
        {
            int wsize[2];
            win->GetSize(wsize[0], wsize[1]);
            GetViewerState()->GetWindowInformation()->SetWindowSize(wsize);
        }

        //
        // Get some rendering information.
        //
        if((flags & WINDOWINFO_RENDERINFO) != 0)
        {
            double times[6] = {0., 0., 0., 0., 0., 0.};
            win->GetRenderTimes(times);
            GetViewerState()->GetWindowInformation()->SetLastRenderMin(times[0]);
            GetViewerState()->GetWindowInformation()->SetLastRenderAvg(times[1]);
            GetViewerState()->GetWindowInformation()->SetLastRenderMax(times[2]);

            // Set the approximate number of triangles.
            GetViewerState()->GetWindowInformation()->SetNumPrimitives(win->GetNumPrimitives());

            // Set the bounding box.
            double extents[6] = {0., 0., 0., 0., 0., 0.};
            if (win->GetWindowMode() == WINMODE_3D)
                win->GetExtents(3, extents);
            else
                win->GetExtents(2, extents);
            GetViewerState()->GetWindowInformation()->SetExtents(extents);
        }

        // Always send back the view dimension or the default value may
        // misinform the client.
        if(win->GetWindowMode() == WINMODE_3D)
            GetViewerState()->GetWindowInformation()->SetViewDimension(3);
        else if (win->GetWindowMode() == WINMODE_CURVE)
            GetViewerState()->GetWindowInformation()->SetViewDimension(1);
        else if (win->GetWindowMode() == WINMODE_2D)
            GetViewerState()->GetWindowInformation()->SetViewDimension(2);
        else
            GetViewerState()->GetWindowInformation()->SetViewDimension(4); // axis array supports >3 dims

        // Allow for some customization of the window information.
        if(UpdateWindowInformationCB != NULL)
        {
            (*UpdateWindowInformationCB)(GetViewerState()->GetWindowInformation(),
                                         flags, UpdateWindowInformationCBData);
        }

        GetViewerState()->GetWindowInformation()->Notify();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SetUpdateWindowInformationCallback
//
// Purpose:
//   Sets a callback that can participate in the UpdateWindowInformation method.
//
// Arguments:
//   cb     : The callback
//   cbdata : The callback data.
//
// Programmer: Brad Whitlock
// Creation:   Wed Sep  3 10:17:47 PDT 2014
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetUpdateWindowInformationCallback(
    void (*cb)(WindowInformation *, int, void *), void *cbdata)
{
    UpdateWindowInformationCB = cb;
    UpdateWindowInformationCBData = cbdata;
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateWindowRenderingInformation
//
// Purpose:
//   Sends information about rendering such as render time and primitve count
//   to the client.
//
// Arguments:
//   windowIndex : The index of the window that caused the update.
//
// Programmer: Brad Whitlock
// Creation:   Fri Jan 23 16:00:25 PST 2004
//
// Modifications:
//   Mark C. Miller, Thu Mar  3 17:38:36 PST 2005
//   Changed GetNumTriangles to GetNumPrimitives
//
//   Brad Whitlock, Tue Jun 21 12:16:25 PDT 2005
//   I made the query for the window size happen with each render but we
//   only send it back to the client if the window size is different from the
//   values that we've already sent.
//
// ****************************************************************************

void
ViewerWindowManager::UpdateWindowRenderingInformation(int windowIndex)
{
    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    ViewerWindow *win = windows[index];
    if(win != 0 && index == activeWindow)
    {
        if(win->GetNotifyForEachRender())
            UpdateWindowInformation(WINDOWINFO_RENDERINFO | WINDOWINFO_WINDOWSIZE);
        else
        {
            // Get the window size. If it differs from what we've stored in
            // the object then send it back to the client.
            int wsize[2];
            win->GetSize(wsize[0], wsize[1]);
            if(GetViewerState()->GetWindowInformation()->GetWindowSize()[0] != wsize[0] ||
               GetViewerState()->GetWindowInformation()->GetWindowSize()[1] != wsize[1])
            {
                GetViewerState()->GetWindowInformation()->SetWindowSize(wsize);
                GetViewerState()->GetWindowInformation()->Notify();
            }
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateViewKeyframeInformation
//
// Purpose:
//   Updates the view keyframes in the window info and notifies the client.
//
// Arguments:
//   notify : Whether the client should be notified.
//
// Programmer: Brad Whitlock
// Creation:   Fri Jan 23 15:37:06 PST 2004
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::UpdateViewKeyframeInformation()
{
    //
    // Update the view keyframe list.
    //
    int        nIndices = 0;
    const int *keyframeIndices = windows[activeWindow]->GetViewKeyframeIndices(nIndices);
    intVector  keyframeIndices2;
    for (int i = 0; i < nIndices; ++i)
        keyframeIndices2.push_back(keyframeIndices[i]);
    GetViewerState()->GetWindowInformation()->SetViewKeyframes(keyframeIndices2);
    GetViewerState()->GetWindowInformation()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::InitWindowLimits
//
//  Purpose:
//      Initialize the window positions and sizes for various window layouts
//      based on the virtual screen size and the size of the window borders.
//
//  Programmer: Eric Brugger
//  Creation:   September 7, 2000
//
//  Modifications:
//    Jeremy Meredith, Fri Jul 20 11:22:25 PDT 2001
//    Added a shift to the x,y positions of windows.
//
//    Eric Brugger, Fri Feb 13 14:14:41 PST 2004
//    Modified the layout code to create rectangular windows that completely
//    fill the available space rather than creating square windows that fill
//    the space as best as possible.  In the case of 1 x 2 and 2 x 4 layouts
//    the height is restricted to the width.
//
//    Eric Brugger, Thu Jun 30 11:43:22 PDT 2005
//    Added a 2 x 3 layout and removed the 4 x 4 layout.  I also changed the
//    window size creation logic to always create the largest square windows
//    in the allowed space.
//
// ****************************************************************************

void
ViewerWindowManager::InitWindowLimits()
{
    //
    // Create the window layouts.
    //
    int       i, j;
    int       borderWidth, borderHeight;
    int       tempWidth, tempHeight, tempSize;
    int       x, y;
    int       cnt;

    borderWidth  = borderLeft + borderRight;
    borderHeight = borderTop  + borderBottom;

    //
    // The layout for one window.
    //
    x          = screenX + borderLeft - shiftX;
    y          = screenY + borderTop  - shiftY;
    tempWidth  = screenWidth - borderWidth;
    tempHeight = screenHeight - borderHeight;
    tempSize   = tempWidth < tempHeight ? tempWidth : tempHeight;
    windowLimits[0][0].x      = x;
    windowLimits[0][0].y      = y;
    windowLimits[0][0].width  = tempSize;
    windowLimits[0][0].height = tempSize;

    //
    // The layout for a 2x2 grid.
    //
    cnt = 0;
    y          = screenY + borderTop - shiftY;
    tempWidth  = (screenWidth/2)  - borderWidth;
    tempHeight = (screenHeight/2) - borderHeight;
    tempSize   = tempWidth < tempHeight ? tempWidth : tempHeight;
    for (i = 0; i < 2; i++)
    {
        x = screenX + borderLeft - shiftX;
        for (j = 0; j < 2; j++)
        {
            windowLimits[1][cnt].x      = x;
            windowLimits[1][cnt].y      = y;
            windowLimits[1][cnt].width  = tempSize;
            windowLimits[1][cnt].height = tempSize;
            x = x + windowLimits[1][cnt].width + borderWidth;
            cnt ++;
        }
        y = y + windowLimits[1][0].height + borderHeight;
    }

    //
    // The layout for a 3x3 grid.
    //
    cnt = 0;
    y          = screenY + borderTop - shiftY;
    tempWidth  = (screenWidth/3)  - borderWidth;
    tempHeight = (screenHeight/3) - borderHeight;
    tempSize   = tempWidth < tempHeight ? tempWidth : tempHeight;
    for (i = 0; i < 3; i++)
    {
        x = screenX + borderLeft - shiftX;
        for (j = 0; j < 3; j++)
        {
            windowLimits[2][cnt].x      = x;
            windowLimits[2][cnt].y      = y;
            windowLimits[2][cnt].width  = tempSize;
            windowLimits[2][cnt].height = tempSize;
            x = x + windowLimits[2][cnt].width + borderWidth;
            cnt ++;
        }
        y = y + windowLimits[2][0].height + borderHeight;
    }

    //
    // The layout for a 1x2 grid.
    //
    cnt = 0;
    x          = screenX + borderLeft - shiftX;
    y          = screenY + borderTop  - shiftY;
    tempWidth  = (screenWidth/2) - borderWidth;
    tempHeight =  screenHeight   - borderHeight;
    tempSize   = tempWidth < tempHeight ? tempWidth : tempHeight;
    for (i = 0; i < 2; i++)
    {
        windowLimits[3][cnt].x      = x;
        windowLimits[3][cnt].y      = y;
        windowLimits[3][cnt].width  = tempSize;
        windowLimits[3][cnt].height = tempSize;
        x = x + windowLimits[3][cnt].width + borderWidth;
        cnt ++;
    }

    //
    // The layout for a 2x3 grid.
    //
    cnt = 0;
    y          = screenY + borderTop - shiftY;
    tempWidth  = (screenWidth/3)  - borderWidth;
    tempHeight = (screenHeight/2) - borderHeight;
    tempSize   = tempWidth < tempHeight ? tempWidth : tempHeight;
    for (i = 0; i < 2; i++)
    {
        x = screenX + borderLeft - shiftX;
        for (j = 0; j < 3; j++)
        {
            windowLimits[4][cnt].x      = x;
            windowLimits[4][cnt].y      = y;
            windowLimits[4][cnt].width  = tempSize;
            windowLimits[4][cnt].height = tempSize;
            x = x + windowLimits[4][cnt].width + borderWidth;
            cnt ++;
        }
        y = y + windowLimits[4][0].height + borderHeight;
    }

    //
    // The layout for a 2x4 grid.
    //
    cnt = 0;
    y          = screenY + borderTop - shiftY;
    tempWidth  = (screenWidth/4)  - borderWidth;
    tempHeight = (screenHeight/2) - borderHeight;
    tempSize   = tempWidth < tempHeight ? tempWidth : tempHeight;
    for (i = 0; i < 2; i++)
    {
        x = screenX + borderLeft - shiftX;
        for (j = 0; j < 4; j++)
        {
            windowLimits[5][cnt].x      = x;
            windowLimits[5][cnt].y      = y;
            windowLimits[5][cnt].width  = tempSize;
            windowLimits[5][cnt].height = tempSize;
            x = x + windowLimits[5][cnt].width + borderWidth;
            cnt ++;
        }
        y = y + windowLimits[5][0].height + borderHeight;
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SimpleAddWindow
//
//  Purpose:
//    Add a simple window.
//
//  Returns:    The index of the newly added window.
//
//  Programmer: Eric Brugger
//  Creation:   April 11, 2003
//
//  Modifications:
//    Eric Brugger, Fri Feb 13 14:14:41 PST 2004
//    Modified the routine to use both the width and height for the window
//    size, instead of using with width for both.
//
//    Hank Childs, Mon Feb 14 13:37:59 PST 2005
//    Get around a bug with window manager.  If you offset new windows
//    by 32 pixels, the old window is blanked out.  But if you use 33 pixels,
//    the old window is okay (see '5913).  So we are changing the offset.
//
// ****************************************************************************

int
ViewerWindowManager::SimpleAddWindow()
{
    //
    // Check that we have available windows.
    //
    if (nWindows >= maxWindows)
    {
        return -1;
    }

    //
    // Find the first unused window.
    //
    int       windowIndex;

    for (windowIndex = 0; windowIndex < maxWindows; windowIndex++)
    {
        if (windows[windowIndex] == 0) break;
    }

    //
    // Determine the position and size of the new window.
    //
    int       x, y;
    int       width, height;

    if (windowIndex < layout)
    {
        x      = windowLimits[layoutIndex][windowIndex].x;
        y      = windowLimits[layoutIndex][windowIndex].y;
        width  = windowLimits[layoutIndex][windowIndex].width;
        height = windowLimits[layoutIndex][windowIndex].height;
    }
    else
    {
        int offset = 33; // Don't change this number without seeing '5913.
        x      = windowLimits[0][0].x + (nWindows - layout + 1) * offset;
        y      = windowLimits[0][0].y + (nWindows - layout + 1) * offset;
        width  = windowLimits[layoutIndex][0].width;
        height = windowLimits[layoutIndex][0].height;
    }

    //
    // Create the new window along with its animation.
    //
    CreateVisWindow(windowIndex, width, height, x, y);

    return windowIndex;
}

// ****************************************************************************
//  Method: ViewerWindowManager::CreateVisWindow
//
//  Purpose:
//    Create a window at the specified size and location.
//
//  Arguments:
//    windowIndex  The 0-origin index of the window to create.
//    width        The window's width.
//    height       The window's height.
//    x            The window's x position.
//    y            The window's y position.
//
//  Programmer: Eric Brugger
//  Creation:   September 20, 2000
//
//  Modifications:
//    Brad Whitlock, Tue Nov 7 10:02:09 PDT 2000
//    Relocated animations to ViewerWindow.
//
//    Brad Whitlock, Mon Nov 27 14:07:50 PST 2000
//    Added code to emit a createWindow signal.
//
//    Jeremy Meredith, Fri Jul 20 11:22:48 PDT 2001
//    Added code to resize/reposition the window after it is realized.
//    Some window managers place them incorrectly the first time they are
//    created, so we must fix it afterwards.
//
//    Jeremy Meredith, Fri Sep 14 13:33:36 PDT 2001
//    Added a preshift to the initial position of the window.
//    Also removed the second setsize call since it was redundant.
//
//    Brad Whitlock, Tue Feb 5 09:56:09 PDT 2002
//    Added code to set the navigation, perspective, autocenter modes of the
//    window from the defaults.
//
//    Sean Ahern, Tue Apr 16 14:06:25 PDT 2002
//    Added the ability to have deferred creation of windows.
//
//    Brad Whitlock, Mon Sep 16 17:40:10 PST 2002
//    I made it use the windowInfo and GetViewerState()->GetRenderingAttributes() state objects.
//
//    Hank Childs, Fri Oct 18 08:22:45 PDT 2002
//    Put all of the code for setting the window attributes into a separate
//    routine.
//
//    Brad Whitlock, Wed Mar 16 13:26:42 PST 2005
//    I set the referenced flag for the window to false so it is right if
//    we delete and recreate a window.
//
//    Brad Whitlock, Wed Apr 30 10:13:32 PDT 2008
//    Support for internationalization.
//
//    Brad Whitlock, Tue Aug 19 16:14:01 PDT 2014
//    Use factory to create the window.
//
// ****************************************************************************

void
ViewerWindowManager::CreateVisWindow(const int windowIndex,
                                     const int width, const int height,
                                     const int x, const int y)
{
    windows[windowIndex] = GetViewerFactory()->CreateViewerWindow(windowIndex);

    windows[windowIndex]->SetSize(width, height);

    ViewerText title(TR("Window %1").arg(windowIndex+1));
    windows[windowIndex]->SetTitle(title.toStdString().c_str());
    if (windowsHidden == false)
    {
        windows[windowIndex]->SetLocation(x - preshiftX, y - preshiftY);
        windows[windowIndex]->Realize();
        windows[windowIndex]->SetLocation(x, y);
    }
    x_locations[windowIndex] = x;
    y_locations[windowIndex] = y;
    referenced[windowIndex] = false;

    nWindows++;

    TRY
    {
        windows[windowIndex]->GetActionManager()->EnableActions(GetWindowAtts());
#ifdef __APPLE__
        windows[windowIndex]->Show();
#endif
    }
    CATCHALL
    {
        ; // nothing
    }
    ENDTRY
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetWindowAttributes
//
//  Purpose:
//      Sets the window attributes, like bounding box mode, autocenter, etc.
//
//  Arguments:
//    windowIndex  The 0-origin index of the window to create.
//    copyAtts     A boolean saying if this should be a clone of the previous
//                 window.
//
//  Notes:      This routine was primarily taken from CreateVisWindow.
//
//  Programmer: Hank Childs
//  Creation:   October 18, 2002
//
//  Modifications:
//    Brad Whitlock, Tue Nov 12 14:50:16 PST 2002
//    I added code to copy the locktime and locktool modes.
//
//    Kathleen Bonnell, Wed Dec  4 17:38:27 PST 2002
//    Removed antialiasing frames, no longer needed.
//
//    Eric Brugger, Fri Apr 18 12:38:05 PDT 2003
//    I removed auto center view.
//
//    Brad Whitlock, Wed May 21 07:52:21 PDT 2003
//    I made fullframe be copied to the new window.
//
//    Jeremy Meredith, Fri Nov 14 17:57:31 PST 2003
//    Added specular properties.
//
//    Hank Childs, Mon May 10 08:10:40 PDT 2004
//    Replace references to immediate mode rendering with display list mode.
//
//   Mark C. Miller, Tue May 11 20:21:24 PDT 2004
//   Modified calls to set scalable controls to accomdate scalable activation
//   mode and scalable auto threshold
//
//    Hank Childs, Sun Oct 24 13:39:57 PDT 2004
//    Added shading properties.
//
//    Jeremy Meredith, Wed Aug 29 15:23:19 EDT 2007
//    Added depth cueing properties.
//
//    Hank Childs, Sat Mar 13 18:46:54 PST 2010
//    Remove reference to bounding box mode.
//
//    Jeremy Meredith, Fri Apr 30 14:39:07 EDT 2010
//    Added automatic depth cueing mode.
//
//    Eric Brugger, Fri Oct 28 10:07:28 PDT 2011
//    Add a multi resolution display capability for AMR data.
//
//    Kathleen Biagas, Thu Apr  2 17:06:22 PDT 2015
//    Ensure color texturing flag gets set.
//
// ****************************************************************************

void
ViewerWindowManager::SetWindowAttributes(int windowIndex, bool copyAtts)
{
    ViewerWindow *w = windows[windowIndex];

    w->SetViewExtentsType((avtExtentType)GetViewerState()->GetWindowInformation()->GetViewExtentsType());
    w->SetPerspectiveProjection(GetViewerState()->GetWindowInformation()->GetPerspective());
    w->SetFullFrameMode(GetViewerState()->GetWindowInformation()->GetFullFrame());
    if (copyAtts)
    {
        w->SetViewIsLocked(GetViewerState()->GetWindowInformation()->GetLockView());
        w->SetTimeLock(GetViewerState()->GetWindowInformation()->GetLockTime());
        w->SetToolLock(GetViewerState()->GetWindowInformation()->GetLockTools());
    }
    else
    {
        w->SetViewIsLocked(false);
        w->SetTimeLock(false);
        w->SetToolLock(false);
    }
    w->SetAntialiasing(GetViewerState()->GetRenderingAttributes()->GetAntialiasing());
    w->SetMultiresolutionMode(GetViewerState()->GetRenderingAttributes()->GetMultiresolutionMode());
    w->SetMultiresolutionCellSize(GetViewerState()->GetRenderingAttributes()->GetMultiresolutionCellSize());
    int rep = (int)GetViewerState()->GetRenderingAttributes()->GetGeometryRepresentation();
    w->SetSurfaceRepresentation(rep);
    w->SetStereoRendering(GetViewerState()->GetRenderingAttributes()->GetStereoRendering(),
        (int)GetViewerState()->GetRenderingAttributes()->GetStereoType());
    w->SetNotifyForEachRender(GetViewerState()->GetRenderingAttributes()->GetNotifyForEachRender());
    w->SetScalableActivationMode(GetViewerState()->GetRenderingAttributes()->GetScalableActivationMode());
    w->SetScalableAutoThreshold(GetViewerState()->GetRenderingAttributes()->GetScalableAutoThreshold());
    w->SetSpecularProperties(GetViewerState()->GetRenderingAttributes()->GetSpecularFlag(),
                             GetViewerState()->GetRenderingAttributes()->GetSpecularCoeff(),
                             GetViewerState()->GetRenderingAttributes()->GetSpecularPower(),
                             GetViewerState()->GetRenderingAttributes()->GetSpecularColor());
    w->SetShadingProperties(GetViewerState()->GetRenderingAttributes()->GetDoShadowing(),
                            GetViewerState()->GetRenderingAttributes()->GetShadowStrength());
    w->SetDepthCueingProperties(GetViewerState()->GetRenderingAttributes()->GetDoDepthCueing(),
                                GetViewerState()->GetRenderingAttributes()->GetDepthCueingAutomatic(),
                                GetViewerState()->GetRenderingAttributes()->GetStartCuePoint(),
                                GetViewerState()->GetRenderingAttributes()->GetEndCuePoint());

    w->SetDepthPeeling(GetViewerState()->GetRenderingAttributes()->GetDepthPeeling());
    w->SetOcclusionRatio(GetViewerState()->GetRenderingAttributes()->GetOcclusionRatio());
    w->SetNumberOfPeels(GetViewerState()->GetRenderingAttributes()->GetNumberOfPeels());
    w->SetColorTexturingFlag(GetViewerState()->GetRenderingAttributes()->GetColorTexturingFlag());

#ifdef HAVE_OSPRAY
    w->SetOsprayRendering(GetViewerState()->GetRenderingAttributes()->GetOsprayRendering());
    w->SetOspraySPP(GetViewerState()->GetRenderingAttributes()->GetOspraySPP());
    w->SetOsprayAO(GetViewerState()->GetRenderingAttributes()->GetOsprayAO());
    w->SetOsprayShadows(GetViewerState()->GetRenderingAttributes()->GetOsprayShadows());
#endif

}

// ****************************************************************************
//  Method: ViewerWindowManager::ViewCallback
//
//  Purpose:
//    This method is a callback function for VisItInteractor. It gets called
//    each time the view changes and is responsible for telling the client
//    about the new view information.
//
//  Arguments:
//    vw        The vis window where the view changed.
//
//  Programmer: Eric Brugger
//  Creation:   August 21, 2001
//
//  Modifications:
//    Eric Brugger, Wed Aug 22 14:52:37 PDT 2001
//    I removed an argument from UpdateViewAtts.
//
//    Hank Childs, Fri Nov 30 18:04:11 PST 2001
//    Update the current window atts whenever we move the mouse.  This was put
//    in so that the volume renderer could determine if it could use the last
//    image or if it must be recalculated.
//
//    Eric Brugger, Fri Apr 18 12:38:05 PDT 2003
//    I added code to set the 2d view as modified if the view dimension is 2.
//
//    Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//    I modified the routine to set curve, 2d and 3d view attributes in the
//    window attributes.  I removed some unused code.
//
//    Eric Brugger, Thu Aug 28 12:22:14 PDT 2003
//    I added a call to mark the view as changed if the window is in
//    curve mode.
//
//    Jeremy Meredith, Thu Jan 31 14:56:06 EST 2008
//    Added new axis array window mode.
//
// ****************************************************************************

void
ViewerWindowManager::ViewCallback(VisWindow *vw)
{
    ViewerWindowManager *instance = ViewerWindowManager::Instance();

    //
    // We may be getting a view callback from something besides the active
    // window.  If so, figure out which window it is and use that.
    //
    int index = -1;
    for (int i = 0 ; i < instance->maxWindows ; i++)
    {
        if (instance->windows[i] != 0 &&
            instance->windows[i]->IsTheSameWindow(vw))
        {
            index = i;
            break;
        }
    }

    //
    // Mark the view as having been modified.
    //
    if (instance->windows[index]->GetWindowMode() == WINMODE_CURVE)
        instance->windows[index]->SetViewModifiedCurve();
    else if(instance->windows[index]->GetWindowMode() == WINMODE_2D)
        instance->windows[index]->SetViewModified2d();
    else if(instance->windows[index]->GetWindowMode() == WINMODE_AXISARRAY)
        instance->windows[index]->SetViewModifiedAxisArray();

    //
    // Update the view attributes in the client and any locked windows.
    //
    instance->UpdateViewAtts(index);

    //
    // For software rendering.
    //
    WindowAttributes winAtts;
    winAtts.SetViewCurve(*GetViewerState()->GetViewCurveAttributes());
    winAtts.SetView2D(*GetViewerState()->GetView2DAttributes());
    winAtts.SetView3D(*GetViewerState()->GetView3DAttributes());
    winAtts.SetViewAxisArray(*GetViewerState()->GetViewAxisArrayAttributes());
    avtCallback::SetCurrentWindowAtts(winAtts);
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetToolEnabled
//
//  Purpose:
//    This is a Qt slot function that sets the enabled state for the tool in
//    the specified window.
//
//  Arguments:
//    toolId       The index of the tool being set.
//    enabled      A flag indicating if the tool is enabled.
//    windowIndex  This is a zero-origin integer that specifies the index
//                 of the window we want to change. If the value is -1, use
//                 use the active window.
//
//  Programmer: Brad Whitlock
//  Creation:   Mon Oct 1 16:15:15 PST 2001
//
// ****************************************************************************

void
ViewerWindowManager::SetToolEnabled(int toolId, bool enabled, int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->SetToolEnabled(toolId, enabled);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::DisableAllTools
//
// Purpose:
//   Disables all of the interactive tools for the specified window.
//
// Arguments:
//   windowIndex : The index of the window whose tools we're disabling.
//
// Programmer: Brad Whitlock
// Creation:   Tue Jul 23 16:16:51 PST 2002
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::DisableAllTools(int windowIndex)
{
    if(windowIndex < -1 || windowIndex >= maxWindows)
        return;

    int index = (windowIndex == -1) ? activeWindow : windowIndex;
    if(windows[index] != 0)
    {
        windows[index]->DisableAllTools();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetPlotColors
//
//  Purpose:
//    Sends background and foreground color information to the viewerwindow,
//    to be used by the plots.  If gradient background is specified in the
//    atts, then the average of the two gradient colors is used.
//
//  Arguments:
//    atts      The annotation attributes containing background color info.
//
//  Programmer: Kathleen Bonnell
//  Creation:   September 26, 2001
//
//  Modifications:
//    Brad Whitlock, Tue Nov 6 17:12:06 PST 2001
//    Modified the code to account for the change in the gradient color
//    representation.
//
//    Eric Brugger, Nov  1 10:32:25 PST 2002
//    Modified the code to account for changes to AnnotationAttributes.
//
//    Mark C. Miller, Wed May 26 12:49:52 PDT 2004
//    Modified to use GetDiscernibleBackground color
//
// ****************************************************************************

void
ViewerWindowManager::SetPlotColors(const AnnotationAttributes *atts)
{
    double bg[4];
    double fg[4];

    atts->GetForegroundColor().GetRgba(fg);
    atts->GetDiscernibleBackgroundColor().GetRgba(bg);
    windows[activeWindow]->SetPlotColors(bg, fg);
}

// ****************************************************************************
//  Method: ViewerWindowManager::ToolCallback
//
//  Purpose:
//    This is a static callback function that handles new information coming
//    from tools.
//
//  Arguments:
//    ti        Information from the tool.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Oct 9 15:12:31 PST 2001
//
//  Modfications:
//    Brad Whitlock, Thu Apr 11 17:36:00 PST 2002
//    I made it care about the "Apply to all operators" flag.
//
//    Brad Whitlock, Tue Nov 12 14:11:55 PST 2002
//    I changed the code so it can lock tools together across windows.
//
//    Brad Whitlock, Wed Mar 12 11:44:08 PDT 2003
//    I made it return early if the engine is executing.
//
// ****************************************************************************

void
ViewerWindowManager::ToolCallback(const avtToolInterface &ti)
{
    //
    // Return without doing anything if the engine is executing.
    //
    if(GetViewerProperties()->GetInExecute())
        return;

    //
    // Let the window that caused the tool "event" handle the event first.
    //
    ViewerWindowManager *wM = ViewerWindowManager::Instance();
    bool applyToAll = GetViewerState()->GetGlobalAttributes()->GetApplyOperator();
    ViewerWindow *toolWin = 0;
    int iWindow;
    for(iWindow = 0; iWindow < wM->maxWindows; iWindow++)
    {
        ViewerWindow *win = wM->windows[iWindow];
        if (win != 0)
        {
            if(win->IsTheSameWindow((VisWindow *)ti.GetVisWindow()))
            {
                toolWin = win;
                toolWin->HandleTool(ti, applyToAll);
                break;
            }
        }
    }

    //
    // Update the other windows if the window that originated the tool
    // event is in locked tool mode.
    //
    if(toolWin != 0 && toolWin->GetToolLock())
    {
        for(iWindow = 0; iWindow < wM->maxWindows; iWindow++)
        {
            ViewerWindow *win = wM->windows[iWindow];
            if (win != 0 && win != toolWin)
            {
                win->HandleTool(ti, applyToAll);
            }
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SetAnimationCallback
//
// Purpose:
//   Set the animation callback to use for animations.
//
// Arguments:
//   cb     : the animation callback.
//   cbdata : The animation callback data.
//
// Returns:
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Tue Sep  2 15:34:38 PDT 2014
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetAnimationCallback(void (*cb)(int,void*),void *cbdata)
{
    AnimationCB = cb;
    AnimationCBData = cbdata;
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateAnimationTimer
//
//  Purpose:
//    This routine determines if the timer for performing animations should
//    be changed (either turned on or off) based on the current state of the
//    timer and the state of all the animations.
//
//  Programmer: Brad Whitlock
//  Creation:   Tue Sep  2 15:32:24 PDT 2014
//
//  Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::UpdateAnimationTimer()
{
    if(AnimationCB != NULL)
        (*AnimationCB)(0, AnimationCBData);
}

// ****************************************************************************
// Method: ViewerWindowManager::StopTimer
//
// Purpose:
//   Turns off the animation timer and makes all animations stop.
//
// Note:       This method is only called when a window is deleted using
//             the window decorations.
//
// Programmer: Brad Whitlock
// Creation:  Tue Sep  2 15:32:24 PDT 2014
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::StopTimer()
{
    if(AnimationCB != NULL)
        (*AnimationCB)(1, AnimationCBData);
}

// ****************************************************************************
// Method: ViewerWindowManager::GetDefaultAnnotationObjectList
//
// Purpose:
//   Returns a pointer to the default annotation object list.
//
// Returns:    A pointer to the annotation object list.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 29 11:20:07 PDT 2003
//
// Modifications:
//
// ****************************************************************************

AnnotationObjectList *
ViewerWindowManager::GetDefaultAnnotationObjectList()
{
    if(defaultAnnotationObjectList == 0)
        defaultAnnotationObjectList = new AnnotationObjectList;

    return defaultAnnotationObjectList;
}

// ****************************************************************************
// Method: ViewerWindowManager::SetDefaultAnnotationObjectListFromClient
//
// Purpose:
//   Copies the client annotation object list into the default annotation
//   object list.
//
// Programmer: Brad Whitlock
// Creation:   Fri Nov 7 14:04:10 PST 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetDefaultAnnotationObjectListFromClient()
{
    *(GetDefaultAnnotationObjectList()) = *(GetViewerState()->GetAnnotationObjectList());
}

// ****************************************************************************
// Method: ViewerWindowManager::GetLineoutWindow
//
// Purpose:
//   Returns a pointer to a window that can be used for lineout.
//   Adds a new window if necessary.
//
// Returns:
//   The lineout window, null if it couldn't be created..
//
// Programmer: Kathleen Bonnell
// Creation:   June 10, 2002
//
// Modifications:
//   Kathleen Bonnell, Tue Jul 23 15:25:22 PDT 2002
//   Initialize the lineout window as a curve.
//
//   Eric Brugger, Wed Apr  9 09:48:25 PDT 2003
//   I added code to mark the window as referenced so that the window
//   attributes would not be copied on first reference.
//
//   Eric Brugger, Fri Apr 11 14:03:19 PDT 2003
//   I modified the routine to use SimpleAddWindow instead of AddWindow
//   to create a new window if it needs to do so.  This eliminates the
//   possibility of copying window attributes and doesn't make the new
//   window active.
//
//   Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//   Removed call to SetTypeIsCurve since it is no longer necessary.
//
//   Brad Whitlock, Mon Jan 26 22:50:15 PST 2004
//   Made it use the plot list directly.
//
//   Mark C. Miller, Mon Jul 12 19:46:32 PDT 2004
//   Added optional bool to immediately return NULL if the window doesn't
//   already exist
//
//   Kathleen Bonnell, Thu Jul 22 15:20:29 PDT 2004
//   Removed bool arg (no longer needed due to changes from ViewerQueryManager)
//   added int arg to specify a particular window to return (or create).
//   Added logic to return specified window (if useThisId not -1).
//
//   Brad Whitlock, Wed Apr 30 10:14:36 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

ViewerWindow *
ViewerWindowManager::GetLineoutWindow(int useThisId)
{
    int returnId = lineoutWindow;
    if (useThisId != -1)
    {
        //
        // Use or create the requested window.
        //
        if (useThisId >= maxWindows || windows[useThisId] == 0)
        {
            int newWin = SimpleAddWindow();
            if (newWin == -1)
            {
                GetViewerMessaging()->Error(
                    TR("VisIt could not open a window for Lineout because "
                       "the maximum number of windows was exceeded."));
                return NULL;
            }
            SetWindowAttributes(newWin, false);
            returnId = newWin;
            windows[returnId]->SetInteractionMode(NAVIGATE);
            referenced[returnId] = true;
        }
        else
        {
            returnId = useThisId;
        }

    }
    else if (lineoutWindow == -1)
    {
        //
        //  Search for an open, empty window.  If none exists,
        //  create one && designate it as the default Lineout window.
        //
        int       winIdx;
        for (winIdx = 0; winIdx < maxWindows; winIdx++)
        {
            if (windows[winIdx] == 0)
            {
                winIdx = -1;
                break;
            }
            if (windows[winIdx]->GetPlotList()->GetNumPlots() == 0)
            {
                break;
            }
        }
        if (winIdx == -1)
        {
            lineoutWindow = SimpleAddWindow();
            SetWindowAttributes(lineoutWindow, false);
        }
        else if (winIdx < maxWindows)
        {
            lineoutWindow = winIdx;
        }
        else
        {
            GetViewerMessaging()->Error(
                TR("VisIt could not open a window for Lineout because the "
                   "maximum number of windows was exceeded."));
            return NULL;
        }
        windows[lineoutWindow]->SetInteractionMode(NAVIGATE);
        referenced[lineoutWindow] = true;
        returnId = lineoutWindow;
    }
    return windows[returnId];
}


// ****************************************************************************
// Method: ViewerWindowManager::ResetLineoutDesignation
//
// Purpose:    Turns off the lineout designation for the active window
//             if it was previously set.  This occurs if all the curve plots
//             have been removed from the window, or the window has been deleted.
//
// Programmer: Kathleen Bonnell
// Creation:   May 7, 2002
//
// Modifications:
//   Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//   Removed call to SetTypeIsCurve since it is no longer necessary.
//
// ****************************************************************************

void ViewerWindowManager::ResetLineoutDesignation(int winIndex)
{
    if (lineoutWindow == -1)
    {
        return;
    }

    if ((winIndex == -1 && activeWindow == lineoutWindow) ||
         winIndex == lineoutWindow)
    {
        lineoutWindow = -1;
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::GetWindowIndices
//
// Purpose:
//   Returns an array that contains the list of valid window indices. The
//   caller is responsible for freeing the memory.
//
// Arguments:
//   nwin : A pointer to a variable that contains the number of window indices.
//
// Returns:    An array of window indices.
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Thu Jun 27 17:23:55 PST 2002
//
// Modifications:
//
// ****************************************************************************

int *
ViewerWindowManager::GetWindowIndices(int *nwin) const
{
    int id = 0;
    int *indices = new int[maxWindows];

    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
            indices[id++] = i;
    }

    *nwin = id;
    return indices;
}

std::vector<ViewerWindow *>
ViewerWindowManager::GetWindows() const
{
    std::vector<ViewerWindow *> W;
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
            W.push_back(windows[i]);
    }
    return W;
}

// ****************************************************************************
// Method: ViewerWindowManager::GetTimeLockedWindowIndices
//
// Purpose:
//   Gets the indices of the locked windows.
//
// Arguments:
//   windowIds : The return vector for the locked window indices.
//
// Programmer: Brad Whitlock
// Creation:   Wed Mar 16 17:35:53 PST 2005
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::GetTimeLockedWindowIndices(intVector &windowIds) const
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0 && windows[i]->GetTimeLock() &&
           windows[i]->GetPlotList()->HasActiveTimeSlider())
            windowIds.push_back(i);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::GetNumWindows
//
// Purpose:
//   Returns the number of windows.
//
// Returns:    The number of windows.
//
// Programmer: Brad Whitlock
// Creation:   Fri Jan 31 12:47:37 PDT 2003
//
// Modifications:
//
// ****************************************************************************

int
ViewerWindowManager::GetNumWindows() const
{
    return nWindows;
}

// ****************************************************************************
// Method: ViewerWindowManager::UpdateActions
//
// Purpose:
//   Updates the actions in all windows.
//
// Programmer: Brad Whitlock
// Creation:   Fri Jan 31 13:13:51 PST 2003
//
// Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::UpdateActions()
{
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
            windows[i]->GetActionManager()->UpdateSingleWindow();
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::BeginEngineExecute
//
// Purpose:
//   Tells the client that an engine is executing.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 30 15:00:11 PST 2002
//
// Modifications:
//   Brad Whitlock, Fri Apr 15 16:23:02 PST 2005
//   Removed code to disable popup menus.
//
// ****************************************************************************

void
ViewerWindowManager::BeginEngineExecute()
{
    //
    // Tell the client that an engine is executing.
    //
    GetViewerProperties()->SetInExecute(true);
    GetViewerState()->GetGlobalAttributes()->SetExecuting(true);
    UpdateGlobalAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::EndEngineExecute
//
// Purpose:
//   Tells the client that an engine is done executing.
//
// Programmer: Brad Whitlock
// Creation:   Wed Oct 30 15:00:11 PST 2002
//
// Modifications:
//   Brad Whitlock, Fri Apr 15 16:23:02 PST 2005
//   Removed code to enable popup menus.
//
// ****************************************************************************

void
ViewerWindowManager::EndEngineExecute()
{
    //
    // Tell the client that an engine is done executing.
    //
    GetViewerProperties()->SetInExecute(false);
    GetViewerState()->GetGlobalAttributes()->SetExecuting(false);
    UpdateGlobalAtts();
}

// ****************************************************************************
// Method: ViewerWindowManager::DisableExternalRenderRequestsAllWindows
//
// Purpose: Temporarily disables external render requests in all windows.
//
// Arguments:
//   oldAble: a vector of bools indicating the previous enabled state of each
//   window's external render requests
//
// Programmer: Mark C. Miller
// Creation:   November 11, 2003
//
// ****************************************************************************

void
ViewerWindowManager::DisableExternalRenderRequestsAllWindows(
    std::vector<bool>& oldAble, bool bClearImage)
{
    for (int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if (windows[windowIndex] != 0)
        {
            oldAble.push_back(windows[windowIndex]->DisableExternalRenderRequests(bClearImage));
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::EnableExternalRenderRequestsAllWindows
//
// Purpose: Re-enable external render requests in all windows.
//
// Arguments:
//   oldAble: a vector of bools indicating the previous enabled state of each
//   window's external render requests
//
// Programmer: Mark C. Miller
// Creation:   November 11, 2003
//
// ****************************************************************************

void
ViewerWindowManager::EnableExternalRenderRequestsAllWindows(
    const std::vector<bool> oldAble)
{
    for (int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
    {
        if (windows[windowIndex] != 0)
        {
            if (oldAble[windowIndex])
                windows[windowIndex]->EnableExternalRenderRequests();
        }
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::CreateNode
//
// Purpose:
//   Lets the ViewerWindowManager save itself to a config file.
//
// Arguments:
//   parentNode : The node to which we're adding information.
//   dbToSource : A map of database names to source ids.
//   detailed   : A flag that tells whether we should write detailed info.
//
// Programmer: Brad Whitlock
// Creation:   Mon Jun 30 12:58:36 PDT 2003
//
// Modifications:
//   Brad Whitlock, Thu Jul 17 14:20:27 PST 2003
//   Added information for a full restart.
//
//   Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//   Added writing out of lineout window.
//
//   Brad Whitlock, Tue Oct 21 14:54:51 PST 2003
//   I prevented the activeWindow and the lineoutWindow from being saved
//   if we're not producing a detailed log.
//
//   Eric Brugger, Fri Dec  5 13:42:39 PST 2003
//   Added writing of maintainView, cameraView and viewExtentsType.
//
//   Eric Brugger, Mon Mar 29 15:21:11 PST 2004
//   Added writing of maintainData.
//
//   Kathleen Bonnell, Thu Apr  1 19:13:59 PST 2004
//   Added timeQueryWindow.
//
//   Brad Whitlock, Thu Nov 9 16:43:00 PST 2006
//   Added dbToSource argument.
//
//   Jeremy Meredith, Wed Feb  3 15:35:08 EST 2010
//   Removed maintain data; moved maintain view from Global settings
//   (Main window) to per-window Window Information (View window).
//
//   Brad Whitlock, Wed Aug 11 16:54:22 PDT 2010
//   I added the selection list.
//
// ****************************************************************************

void
ViewerWindowManager::CreateNode(DataNode *parentNode,
    const std::map<std::string, std::string> &dbToSource, bool detailed)
{
    if(parentNode == 0)
        return;

    DataNode *mgrNode = new DataNode("ViewerWindowManager");
    parentNode->AddNode(mgrNode);

    GetViewerState()->GetSelectionList()->CreateNode(mgrNode, detailed, true);

    //
    // Add information about the ViewerWindowManager.
    //
    if(detailed)
    {
        mgrNode->AddNode(new DataNode("activeWindow", activeWindow));
        mgrNode->AddNode(new DataNode("lineoutWindow", lineoutWindow));
        mgrNode->AddNode(new DataNode("timeQueryWindow", timeQueryWindow));
    }

    //
    // The following attributes are actually per window, but are being
    // treated as global so that they are saved when saving settings.
    //
    mgrNode->AddNode(new DataNode("cameraView",
       windows[activeWindow]->GetCameraViewMode()));
    mgrNode->AddNode(new DataNode("viewExtentsType",
       avtExtentType_ToString(windows[activeWindow]->GetViewExtentsType())));

    //
    // Let each window add its own data.
    //
    DataNode *windowsNode = new DataNode("Windows");
    mgrNode->AddNode(windowsNode);
    for(int i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0)
            windows[i]->CreateNode(windowsNode, dbToSource, detailed);
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SetFromNode
//
// Purpose:
//   Lets the ViewerWindowManager initialize itself from the information stored
//   in a config file's DataNode.
//
// Arguments:
//   parentNode    : The node from which to get information about how to initialize
//                   the ViewerWindowManager.
//   sourceToDB    : The source to DB map.
//   configVersion : The version from the config file.
//
// Programmer: Brad Whitlock
// Creation:   Mon Jun 30 12:56:30 PDT 2003
//
// Modifications:
//   Brad Whitlock, Thu Jul 17 14:25:02 PST 2003
//   Added code to reconstruct all of the windows in the config file.
//
//   Eric Brugger, Wed Aug 20 13:22:14 PDT 2003
//   Removed call to SetTypeIsCurve since it is no longer necessary.
//
//   Brad Whitlock, Mon Aug 25 11:42:49 PDT 2003
//   Added code to temporarily disable "clone window on first reference" when
//   setting the active window once all the windows are established.
//
//   Brad Whitlock, Tue Dec 2 17:13:31 PST 2003
//   Added code to change the referenced flag so windows with plots are not
//   considered unreferenced.
//
//   Eric Brugger, Fri Dec  5 13:42:39 PST 2003
//   Added loading of maintainView, cameraView and viewExtentsType.
//
//   Eric Brugger, Thu Dec 18 14:40:20 PST 2003
//   Modified the routine to set the referenced flag for the active window
//   to true.
//
//   Eric Brugger, Mon Mar 29 15:21:11 PST 2004
//   Added loading of maintainData.
//
//   Kathleen Bonnell, Thu Apr  1 19:13:59 PST 2004
//   Added timeQueryWindow.
//
//   Brad Whitlock, Tue Aug 3 11:42:48 PDT 2004
//   I added code to override the window sizes in the session file if the
//   viewer is run in -nowin mode.
//
//   Brad Whitlock, Fri Nov 10 10:00:49 PDT 2006
//   Added sourceToDB argument.
//
//   Brad Whitlock, Wed Feb 13 14:12:47 PST 2008
//   Added configVersion argument.
//
//   Brad Whitlock, Tue Apr 14 11:29:57 PDT 2009
//   Use ViewerProperties.
//
//   Jeremy Meredith, Wed Feb  3 15:35:08 EST 2010
//   Removed maintain data; moved maintain view from Global settings
//   (Main window) to per-window Window Information (View window).
//
//   Brad Whitlock, Wed Aug 11 16:54:38 PDT 2010
//   I added the selection node and code to regenerate named selections from
//   the plots that generate them.
//
//   Brad Whitlock, Tue Dec 14 13:34:58 PST 2010
//   Pass selection properties when creating named selections.
//
//   Brad Whitlock, Mon Aug 15 10:55:03 PDT 2011
//   I rewrote how we manage window creation/deletion and setting sizes based
//   on information from the config file.
//
//   Brad Whitlock, Fri Aug 19 11:37:00 PDT 2011
//   Fix restoration of selections that are not based on plots.
//
// ****************************************************************************

void
ViewerWindowManager::SetFromNode(DataNode *parentNode,
    const std::map<std::string,std::string> &sourceToDB,
    const std::string &configVersion)
{
    if(parentNode == 0)
        return;

    DataNode *searchNode = parentNode->GetNode("ViewerWindowManager");
    if(searchNode == 0)
        return;

    // Load the selection list
    GetViewerState()->GetSelectionList()->SetFromNode(searchNode);
    GetViewerState()->GetSelectionList()->Notify();

    //
    // Load information specific to ViewerWindowManager.  The following
    // attributes are actually per window, but are being treated as
    // global so that they are saved when saving settings.
    //
    DataNode *node = 0;
    if((node = searchNode->GetNode("cameraView")) != 0)
        windows[activeWindow]->SetCameraViewMode(node->AsBool());
    if((node = searchNode->GetNode("viewExtentsType")) != 0)
    {
        // Allow enums to be int or string in the config file
        if(node->GetNodeType() == INT_NODE)
        {
            int ival = node->AsInt();
            ival = (ival < 0 || ival > 3) ? 0 : ival;
            windows[activeWindow]->SetViewExtentsType(avtExtentType(ival));
        }
        else if(node->GetNodeType() == STRING_NODE)
        {
            avtExtentType value;
            if(avtExtentType_FromString(node->AsString(), value))
                windows[activeWindow]->SetViewExtentsType(value);
        }
    }

    //
    // Create the right number of windows.
    //
    DataNode *windowsNode = searchNode->GetNode("Windows");
    if(windowsNode == 0)
        return;

    int i;
    DataNode *sizeNode = 0;
    DataNode *locationNode = 0;

    //
    // Get the number of viewer windows that we've found in the Windows node.
    // Print to the debug logs if we get an object that is *not* a
    // ViewerWindow.
    //
    int newNWindows = 0;
    DataNode **vWindowNodes = windowsNode->GetChildren();
    for(int k = 0; k < windowsNode->GetNumChildren(); ++k)
    {
        if(vWindowNodes[k]->GetKey() == "ViewerWindow")
            ++newNWindows;
        else
        {
            debug1 << "ViewerWindowManager::SetFromNode: Bad Window Name!"
                   << "name=" << vWindowNodes[k]->GetKey().c_str() << endl;
        }
    }

    // Read window sizes and locations.
    int nWindowSizeLoc = 0;
    std::vector<int> windowSizeLoc;
    for(i = 0; i < newNWindows; ++i)
    {
        DataNode *windowINode = windowsNode->GetChildren()[i];
        if((sizeNode = windowINode->GetNode("windowSize")) != 0 &&
           (locationNode = windowINode->GetNode("windowLocation")) != 0)
        {
            if(GetViewerProperties()->GetNowin())
            {
                windowSizeLoc.push_back(windowLimits[0][0].width);
                windowSizeLoc.push_back(windowLimits[0][0].height);
                windowSizeLoc.push_back(0);
                windowSizeLoc.push_back(0);
            }
            else
            {
                windowSizeLoc.push_back(sizeNode->AsIntArray()[0]);
                windowSizeLoc.push_back(sizeNode->AsIntArray()[1]);
                windowSizeLoc.push_back(locationNode->AsIntArray()[0]);
                windowSizeLoc.push_back(locationNode->AsIntArray()[1]);
            }
            ++nWindowSizeLoc;
        }
    }

    // Delete excess windows.
    if(newNWindows < nWindows)
    {
        int deleteWindows = nWindows - newNWindows;
        for(i = 0; i < deleteWindows; ++i)
            DeleteWindow();
    }

    // Loop over the windows we have and set their sizes and locations.
    int windowSizeIdx = 0;
    for(i = 0; i < maxWindows; ++i)
    {
        if(windows[i] != 0 && windowSizeIdx < nWindowSizeLoc)
        {
            windows[i]->SetSize(windowSizeLoc[windowSizeIdx*4],
                                windowSizeLoc[windowSizeIdx*4+1]);
            windows[i]->SetLocation(windowSizeLoc[windowSizeIdx*4+2],
                                    windowSizeLoc[windowSizeIdx*4+3]);
            windowSizeIdx++;
        }
    }

    // Create additional windows. Create them the right size if possible.
    if(nWindows < newNWindows)
    {
        int additionalWindows = newNWindows - nWindows;
        for(i = 0; i < additionalWindows; ++i, ++windowSizeIdx)
        {
            if(windowSizeIdx < nWindowSizeLoc)
            {
                for(int windowIndex = 0; windowIndex < maxWindows; ++windowIndex)
                {
                    if(windows[windowIndex] == 0)
                    {
                        int w = windowSizeLoc[windowSizeIdx*4];
                        int h = windowSizeLoc[windowSizeIdx*4+1];
                        int x = windowSizeLoc[windowSizeIdx*4+2];
                        int y = windowSizeLoc[windowSizeIdx*4+3];

                        // Create the vis window so that it has the
                        // right size and location.
                        CreateVisWindow(windowIndex, w, h, x, y);

                        // HACK - set the location again because it could
                        // be shifted a little by some window managers.
                        windows[windowIndex]->SetLocation(x, y);
                        break;
                    }
                }
            }
            else
                AddWindow(false);
        }
    }

    //
    // Load window-specific information.
    //
    DataNode **wNodes = windowsNode->GetChildren();
    int childCount = 0;
    bool *doUpdate = new bool[maxWindows];
    for(i = 0; i < maxWindows; ++i)
    {
        doUpdate[i] = false;
        referenced[i] = false;
        if(windows[i] != 0 && childCount < newNWindows)
        {
            doUpdate[i] = windows[i]->SetFromNode(wNodes[childCount++], sourceToDB, configVersion);
            if(windows[i]->GetPlotList()->GetNumPlots() > 0)
                referenced[i] = true;
        }
    }

    //
    // If we have selections then iterate over all of them.
    //
    if(GetViewerState()->GetSelectionList()->GetNumSelections() > 0)
    {
        bool tmpApply = GetViewerState()->GetSelectionList()->GetAutoApplyUpdates();
        GetViewerState()->GetSelectionList()->SetAutoApplyUpdates(false);

        // Look over the plots in all windows and save the indices of the
        // originating plots for a selection. Save the name too.
        intVector *originatingPlots = new intVector[maxWindows];
        std::vector<SelectionProperties> *selProps = new std::vector<SelectionProperties>[maxWindows];

        for(int k = 0; k < GetViewerState()->GetSelectionList()->GetNumSelections(); ++k)
        {
            const SelectionProperties &props = GetViewerState()->GetSelectionList()->GetSelections(k);
            bool notFound = true;

            for(i = 0; i < maxWindows && notFound; ++i)
            {
                if(windows[i] == 0)
                    continue;
                for(int j = 0; j < windows[i]->GetPlotList()->GetNumPlots() && notFound; ++j)
                {
                    if(props.GetSource() ==
                       windows[i]->GetPlotList()->GetPlot(j)->GetPlotName())
                    {
                        selProps[i].push_back(props);
                        originatingPlots[i].push_back(j);
                        notFound = false;
                    }
                }
            }

            if(notFound)
            {
                // This selection does not have an originating plot, which means
                // that it must be a selection directly on the database. Set it up.
                TRY
                {
                    debug4 << "Creating named selection " << props.GetName()
                           << " from database " << props.GetSource() << endl;

                    std::string host, db, sim, src(props.GetSource());
                    GetViewerFileServer()->ExpandDatabaseName(src, host, db);

                    const avtDatabaseMetaData *md = GetViewerFileServer()->GetMetaData(host, db);
                    if (md != NULL && md->GetIsSimulation())
                        sim = db;

                    EngineKey engineKey(host, sim);
                    SelectionProperties P(props);
                    P.SetSource(db);

                    // We're doing a selection based directly on the database. We need to
                    // send the expression definitions to the engine since we haven't yet
                    // created any plots.
                    ExpressionList exprList;
                    GetViewerStateManager()->GetVariableMethods()->
                        GetAllExpressions(exprList, host, db,
                                          ViewerFileServerInterface::ANY_STATE);
                    GetViewerEngineManager()->UpdateExpressions(engineKey, exprList);

                    // Remove the summary if it is there.
                    int sindex = GetViewerState()->GetSelectionList()->
                                 GetSelectionSummary(P.GetName());
                    if(sindex >= 0)
                        GetViewerState()->GetSelectionList()->RemoveSelectionSummary(sindex);

                    SelectionSummary summary;
                    if(GetViewerEngineManager()->CreateNamedSelection(engineKey, -1, P, summary))
                        GetViewerState()->GetSelectionList()->AddSelectionSummary(summary);
                }
                CATCH(VisItException)
                {
                    GetViewerMessaging()->Error(
                        TR("Could not create named selection %1.").arg(props.GetName()));
                }
                ENDTRY
            }
        }

        // Update the originating plots in windows that contain them and use
        // them to create the named selections they define.
        for(i = 0; i < maxWindows; ++i)
        {
            const intVector &origPlots = originatingPlots[i];
            if(!origPlots.empty())
            {
                // Cause this window's originating plots to be created.
                windows[i]->DisableUpdates();
                windows[i]->GetPlotList()->UpdateFrameForPlots(origPlots);

                // Now create the selections that use those originating plots.
                for(size_t j = 0; j < origPlots.size(); ++j)
                {
                    TRY
                    {
                        // Remove the summary if it is there.
                        int sindex = GetViewerState()->GetSelectionList()->
                                     GetSelectionSummary(selProps[i][j].GetName());
                        if(sindex >= 0)
                            GetViewerState()->GetSelectionList()->RemoveSelectionSummary(sindex);

                        debug4 << "Creating named selection " << selProps[i][j].GetName()
                               << " from plot " << origPlots[j] << " in window "
                               << i << endl;

                        SelectionSummary summary;
                        if(GetViewerEngineManager()->CreateNamedSelection(
                            windows[i]->GetPlotList()->GetPlot(origPlots[j])->GetEngineKey(),
                            windows[i]->GetPlotList()->GetPlot(origPlots[j])->GetNetworkID(),
                            selProps[i][j], summary))
                        {
                            GetViewerState()->GetSelectionList()->AddSelectionSummary(summary);
                        }
                    }
                    CATCH(VisItException)
                    {
                        GetViewerMessaging()->Error(
                            TR("Could not create named selection %1.").arg(selProps[i][j].GetName()));
                    }
                    ENDTRY
                }

                windows[i]->EnableUpdates();
            }
        }

        GetViewerState()->GetSelectionList()->SetAutoApplyUpdates(tmpApply);
        GetViewerState()->GetSelectionList()->Notify();
        delete [] originatingPlots;
        delete [] selProps;
    }

    //
    // For windows that need it, send an update message so the window gets its
    // plots realized.
    //
    for(i = 0; i < maxWindows; ++i)
    {
        if(doUpdate[i])
            windows[i]->SendUpdateFrameMessage();
    }
    delete [] doUpdate;

    //
    // Set the active window.
    //
    if((node = searchNode->GetNode("activeWindow")) != 0)
    {
        int n = node->AsInt();
        if(n >= 0 && n < nWindows && windows[n] != 0)
        {
            referenced[n] = true;
            SetActiveWindow(n + 1);
        }
        else
        {
            referenced[activeWindow] = true;
            UpdateAllAtts();
        }
    }
    else
    {
        referenced[activeWindow] = true;
        UpdateAllAtts();
    }

    //
    // Set the lineout window.
    //
    if((node = searchNode->GetNode("lineoutWindow")) != 0)
    {
        int n = node->AsInt();
        if (n < 0 || n >= maxWindows)
            lineoutWindow = -1;
        else
            lineoutWindow = n;
    }

    //
    // Set the timeQuery window.
    //
    if((node = searchNode->GetNode("timeQueryWindow")) != 0)
    {
        int n = node->AsInt();
        if (n < 0 || n >= maxWindows)
            timeQueryWindow = -1;
        else
            timeQueryWindow = n;
    }
}

// ****************************************************************************
// Method: ViewerWindowManager::SessionContainsErrors
//
// Purpose:
//   Detects whether the session file contains errors.
//
// Arguments:
//   parentNode : The data node to check.
//
// Returns:    True if there are errors; false if otherwise.
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Wed Jan 11 15:10:44 PST 2006
//
// Modifications:
//   Brad Whitlock, Thu Feb 14 14:44:40 PST 2008
//   Fixed since GetNode no longer recursively returns children.
//
// ****************************************************************************

bool
ViewerWindowManager::SessionContainsErrors(DataNode *parentNode)
{
    bool fatalError = true;

    if(parentNode == 0)
        return fatalError;

    DataNode *windowMgrNode = parentNode->GetNode("ViewerWindowManager");
    if(windowMgrNode == 0)
        return fatalError;

    DataNode *windowsNode = windowMgrNode->GetNode("Windows");
    if(windowsNode == 0)
        return fatalError;

    //
    // Iterate over the windows enumerated in the session and look for errors.
    //
    fatalError = false;
    DataNode **wNodes = windowsNode->GetChildren();
    for(int k = 0; k < windowsNode->GetNumChildren() && !fatalError; ++k)
    {
        if(wNodes[k]->GetKey() == "ViewerWindow")
            fatalError = ViewerWindow::SessionContainsErrors(wNodes[k]);
    }

    return fatalError;
}

// ****************************************************************************
// Method: ViewerWindowManager::GetEmptyWindow
//
// Purpose:
//   Returns a pointer to an empy window.
//
// Returns:
//   The empty window, null if it couldn't be created..
//
// Programmer: Kathleen Bonnell
// Creation:   March 19, 2004
//
// Modifications:
//   Brad Whitlock, Wed Apr 30 10:16:27 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

ViewerWindow *
ViewerWindowManager::GetEmptyWindow()
{
    //
    //  Search for an open, empty window.  If none exists,
    //  create one.
    //
    int       winIdx;
    for (winIdx = 0; winIdx < maxWindows; winIdx++)
    {
        if (windows[winIdx] == 0)
        {
                winIdx = -1;
                break;
        }
        if (windows[winIdx]->GetPlotList()->GetNumPlots() == 0)
        {
                break;
        }
    }
    ViewerText msg(TR("The maximum number of windows are already being used."));
    if (winIdx == -1)
    {
        winIdx = SimpleAddWindow();
        if (winIdx == -1)
        {
            GetViewerMessaging()->Error(msg);
            return NULL;
        }
        SetWindowAttributes(winIdx, false);
    }
    else if (winIdx >= maxWindows)
    {
        GetViewerMessaging()->Error(msg);
        return NULL;
    }
    windows[winIdx]->SetInteractionMode(NAVIGATE);
    referenced[winIdx] = true;
    return windows[winIdx];
}


// ****************************************************************************
// Method: ViewerWindowManager::GetWindow
//
// Purpose:
//   Returns a pointer to the window specified by windowIndex.
//
// Arguments:
//   windowIndex  The index of the window to return.
//
// Returns:
//   The specified window, null if the index is invalid.
//
// Programmer: Kathleen Bonnell
// Creation:   March 19, 2004
//
// Modifications:
//
// ****************************************************************************

ViewerWindow *
ViewerWindowManager::GetWindow(int windowIndex)
{
    if(windowIndex < 0 || windowIndex >= maxWindows)
        return NULL;

    return windows[windowIndex];
}


// ****************************************************************************
// Method: ViewerWindowManager::GetTimeQueryWindow
//
// Purpose:
//   Returns a pointer to a window that can be used for queries-through-time.
//   Adds a new window if necessary.
//
// Returns:
//   The timequery window, null if it couldn't be created..
//
// Programmer: Kathleen Bonnell
// Creation:   April 1, 2004
//
// Modifications:
//   Kathleen Bonnell, Tue Jul 20 10:47:26 PDT 2004
//   Added optional arg specifiying the windowId to retrieve.
//
//   Brad Whitlock, Wed Apr 30 10:17:37 PDT 2008
//   Support for internationalization.
//
// ****************************************************************************

ViewerWindow *
ViewerWindowManager::GetTimeQueryWindow(int useThisId)
{
    ViewerText msg(TR("VisIt could not open a window for TimeQuery because "
                      "the maximum number of windows was exceeded."));

    int returnId = timeQueryWindow;
    if (useThisId != -1)
    {
        if (useThisId >= maxWindows || windows[useThisId] == 0)
        {
            int newWin = SimpleAddWindow();
            if (newWin == -1)
            {
                GetViewerMessaging()->Error(msg);
                return NULL;
            }
            SetWindowAttributes(newWin, false);
            returnId = newWin;
            windows[returnId]->SetInteractionMode(NAVIGATE);
            referenced[returnId] = true;
        }
        else
        {
            returnId = useThisId;
        }
    }
    else if (timeQueryWindow == -1)
    {
        //
        //  Search for an open, empty window.  If none exists,
        //  create one.
        //
        int       winIdx;
        for (winIdx = 0; winIdx < maxWindows; winIdx++)
        {
            if (windows[winIdx] == 0)
            {
                winIdx = -1;
                break;
            }
            if (windows[winIdx]->GetPlotList()->GetNumPlots() == 0)
            {
                break;
            }
        }
        if (winIdx == -1)
        {
            timeQueryWindow = SimpleAddWindow();
            SetWindowAttributes(timeQueryWindow, false);
        }
        else if (winIdx < maxWindows)
        {
            timeQueryWindow = winIdx;
        }
        else
        {
            GetViewerMessaging()->Error(msg);
            return NULL;
        }
        windows[timeQueryWindow]->SetInteractionMode(NAVIGATE);
        referenced[timeQueryWindow] = true;
        returnId = timeQueryWindow;
    }
    return windows[returnId];
}


// ****************************************************************************
// Method: ViewerWindowManager::ResetTimeQueryDesignation
//
// Purpose:    Turns off the timequery designation for the active window
//             if it was previously set.  This occurs if all the curve plots
//             have been removed from the window, or the window has been deleted.
//
// Programmer: Kathleen Bonnell
// Creation:   April 1, 2004
//
// Modifications:
//
// ****************************************************************************

void ViewerWindowManager::ResetTimeQueryDesignation(int winIndex)
{
    if (timeQueryWindow == -1)
    {
        return;
    }

    if ((winIndex == -1 && activeWindow == timeQueryWindow) ||
         winIndex == timeQueryWindow)
    {
        timeQueryWindow = -1;
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::GetInteractorDefaultAtts
//
//  Purpose:
//    Returns a pointer to the default interactor attributes.
//
//  Returns:    A pointer to the default interactor attributes.
//
//  Programmer: Kathleen Bonnell
//  Creation:   August 16, 2004
//
// ****************************************************************************

InteractorAttributes *
ViewerWindowManager::GetInteractorDefaultAtts()
{
    //
    // If the client attributes haven't been allocated then do so.
    //
    if (interactorDefaultAtts == 0)
    {
        interactorDefaultAtts = new InteractorAttributes;
    }

    return interactorDefaultAtts;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetClientInteractorAttsFromDefault
//
//  Purpose:
//    This method copies the default interactor attributes into the client
//    interactor attributes.
//
//  Programmer: Kathleen Bonnell
//  Creation:   August 16, 2004
//
// ****************************************************************************

void
ViewerWindowManager::SetClientInteractorAttsFromDefault()
{
    if(interactorDefaultAtts != 0 && GetViewerState()->GetInteractorAttributes() != 0)
    {
        *GetViewerState()->GetInteractorAttributes() = *interactorDefaultAtts;
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetDefaultInteractorAttsFromClient
//
//  Purpose:
//    This method copies the client's interactor attributes into the default
//    interactor attributes.
//
//  Programmer: Kathleen Bonnell
//  Creation:   August 16, 2004
//
// ****************************************************************************

void
ViewerWindowManager::SetDefaultInteractorAttsFromClient()
{
    if(interactorDefaultAtts != 0 && GetViewerState()->GetInteractorAttributes() != 0)
    {
        *interactorDefaultAtts = *GetViewerState()->GetInteractorAttributes();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetInteractorAttsFromClient
//
//  Purpose:
//    Sets the interactor attributes for the active window based on the
//    client's interactor attributes.
//
//  Programmer: Kathleen Bonnell
//  Creation:   August 16, 2004
//
//  Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::SetInteractorAttsFromClient()
{
    for (int i = 0; i < maxWindows; i++)
    {
        if (windows[i] != 0)
            windows[i]->SetInteractorAtts(GetViewerState()->GetInteractorAttributes());
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetInteractorAttsFromDefault
//
//  Purpose:
//    Sets the interactor attributes for the active window based on the
//    default interactor attributes.
//
//
//  Programmer: Kathleen Bonnell
//  Creation:   August 16, 2004
//
// ****************************************************************************

void
ViewerWindowManager::SetInteractorAttsFromDefault()
{
    for (int i = 0; i < maxWindows; i++)
    {
        if (windows[i] != 0)
            windows[i]->SetInteractorAtts(interactorDefaultAtts);
    }
    //
    // Update the client's annotation attributes
    //
    *GetViewerState()->GetInteractorAttributes() = *interactorDefaultAtts;
    GetViewerState()->GetInteractorAttributes()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::UpdateInteractorAtts
//
//  Purpose:
//    Sends the interactor attributes for the active window to the client.
//
//  Programmer: Kathleen Bonnell
//  Creation:   August 16, 2004
//
//  Modifications:
//
// ****************************************************************************

void
ViewerWindowManager::UpdateInteractorAtts()
{
    ViewerWindow *win = windows[activeWindow];
    const InteractorAttributes *winAtts = win->GetInteractorAtts();

    //
    // Copy the window's interactor attributes to the client interactor
    // attributes and notify the client.
    //
    *GetViewerState()->GetInteractorAttributes() = *winAtts;
    GetViewerState()->GetInteractorAttributes()->Notify();
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetTryHarderCyclesTimes
//
//  Programmer: Mark C. Miller
//  Creation:   August 22, 2007
//
// ****************************************************************************

bool ViewerWindowManager::SetTryHarderCyclesTimes(bool newVal)
{
    bool oldVal = GetViewerState()->GetGlobalAttributes()->GetTryHarderCyclesTimes();
    if (oldVal != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetTryHarderCyclesTimes(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
    return oldVal;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetTreatAllDBsAsTimeVarying
//
//  Programmer: Mark C. Miller
//  Creation:   August 22, 2007
//
// ****************************************************************************

bool ViewerWindowManager::SetTreatAllDBsAsTimeVarying(bool newVal)
{
    bool oldVal = GetViewerState()->GetGlobalAttributes()->GetTreatAllDBsAsTimeVarying();
    if (oldVal != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetTreatAllDBsAsTimeVarying(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
    return oldVal;
}


// ****************************************************************************
//  Method: ViewerWindowManager::SetCreateMeshQualityExpressions
//
//  Programmer: Kathleen Bonnell
//  Creation:   October 9, 2007
//
// ****************************************************************************

bool ViewerWindowManager::SetCreateMeshQualityExpressions(bool newVal)
{
    bool oldVal = GetViewerState()->GetGlobalAttributes()->GetCreateMeshQualityExpressions();
    if (oldVal != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetCreateMeshQualityExpressions(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
    return oldVal;
}


// ****************************************************************************
//  Method: ViewerWindowManager::SetCreateExpressions
//
//  Programmer: Kathleen Bonnell
//  Creation:   October 9, 2007
//
// ****************************************************************************

bool ViewerWindowManager::SetCreateTimeDerivativeExpressions(bool newVal)
{
    bool oldVal = GetViewerState()->GetGlobalAttributes()->GetCreateTimeDerivativeExpressions();
    if (oldVal != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetCreateTimeDerivativeExpressions(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
    return oldVal;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetCreateVectorMagnitudeExpressions
//
//  Programmer: Cyrus Harrison
//  Creation:   November 28, 2007
//
// ****************************************************************************

bool ViewerWindowManager::SetCreateVectorMagnitudeExpressions(bool newVal)
{
    bool oldVal = GetViewerState()->GetGlobalAttributes()->GetCreateVectorMagnitudeExpressions();
    if (oldVal != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetCreateVectorMagnitudeExpressions(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
    return oldVal;
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetPrecisionType
//
//  Programmer: Kathleen Biagas
//  Creation:   August 7, 2013
//
// ****************************************************************************

void ViewerWindowManager::SetPrecisionType(int newVal)
{
    if (GetViewerState()->GetGlobalAttributes()->GetPrecisionType() != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetPrecisionType(GlobalAttributes::PrecisionType(newVal));
        GetViewerEngineManager()->UpdatePrecisionType(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetBackendType
//
//  Programmer: Cameron Christensen
//  Creation:   June 10, 2014
//
// ****************************************************************************

void ViewerWindowManager::SetBackendType(int newVal)
{
    if (GetViewerState()->GetGlobalAttributes()->GetBackendType() != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetBackendType(GlobalAttributes::BackendType(newVal));
        GetViewerEngineManager()->UpdateBackendType(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
}

// ****************************************************************************
//  Method: ViewerWindowManager::SetRemoveDuplicateNodes
//
//  Programmer: Kathleen Biagas
//  Creation:   December 18, 2014
//
// ****************************************************************************

void ViewerWindowManager::SetRemoveDuplicateNodes(bool newVal)
{
    if (GetViewerState()->GetGlobalAttributes()->GetRemoveDuplicateNodes() != newVal)
    {
        GetViewerState()->GetGlobalAttributes()->SetRemoveDuplicateNodes(newVal);
        GetViewerEngineManager()->UpdateRemoveDuplicateNodes(newVal);
        GetViewerState()->GetGlobalAttributes()->Notify();
    }
}


// ****************************************************************************
//  Method: ViewerWindowManager::CheckForOSPRayRendering
//
//  Purpose: Checks if ospray was enabled from the command line via avtCallback
//           and sets the RenderingAtts and ViewerWindow states accordingly.
//
//  Note:    Should be called after session files are processed, so that the
//           session file settings for rendering atts don't override the
//           command line
//
//  Programmer: Kathleen Biagas
//  Creation:   Aug 17, 2022
//
// ****************************************************************************

void
ViewerWindowManager::CheckForOSPRayRendering() const
{
#ifdef HAVE_OSPRAY
    if(avtCallback::GetUseOSPRay())
    {
        GetViewerState()->GetRenderingAttributes()->SetOsprayRendering(true);
        GetViewerState()->GetRenderingAttributes()->Notify();
        for (int i = 0; i < maxWindows; i++)
        {
            if (windows[i] != 0)
                windows[i]->SetOsprayRendering(true);
        }
    }
#endif
}

